# EnCoach Odoo 19 Backend -- Developer SRS v3

> **SUPERSEDED** -- This document has been replaced by `ENCOACH_ODOO19_BACKEND_SRS.md` (v3.0) and `ENCOACH_UNIFIED_SRS.md` (v2.0). All content below is historical. The developer has implemented everything and the system is deployed at `http://5.189.151.117:8069`.

**Document Version:** 3.0
**Date:** March 11, 2026
**Status:** ~~Active -- Ready for Development~~ **SUPERSEDED**
**Supersedes:** `ODOO_MIGRATION_SRS_v2.md` (v2.0)
**Master Reference:** `ENCOACH_UNIFIED_SRS.md` (v1.0)
**Author:** EnCoach Architecture Team

---

## Purpose

This document is the **backend implementation specification** for the Odoo 19 developer. It defines every API endpoint, data model, and module the frontend expects. The frontend is **fully built and wired** -- it makes real HTTP calls to these endpoints via TanStack Query. Your job is to ensure every endpoint listed here exists, returns the correct response shape, and connects to the correct Odoo models.

**Source of truth for response shapes:** `encoach_frontend_new/src/types/*.ts` (14 files)
**Source of truth for endpoint paths:** `encoach_frontend_new/src/services/*.ts` (21 files)

---

## Table of Contents

1. [What Already Exists](#1-what-already-exists)
2. [What Needs to Be Built or Modified](#2-what-needs-to-be-built-or-modified)
3. [New Odoo Modules](#3-new-odoo-modules)
4. [Existing Module Modifications](#4-existing-module-modifications)
5. [Complete API Contract](#5-complete-api-contract)
6. [Data Model Specification](#6-data-model-specification)
7. [AI Service Integration](#7-ai-service-integration)
8. [Response Format Standards](#8-response-format-standards)
9. [Non-Functional Requirements](#9-non-functional-requirements)
10. [Implementation Priority](#10-implementation-priority)

---

## 1. What Already Exists

### 1.1 Existing Odoo Modules (15)

These modules are built and deployed in `ielts-be-v0/`:

| # | Module | Controller File | Endpoints |
|---|--------|----------------|-----------|
| 1 | `encoach_core` | -- | Users, entities, roles, permissions, codes, invites |
| 2 | `encoach_ai` | -- | OpenAI service wrapper, constants, blacklist |
| 3 | `encoach_ai_media` | `media.py` | TTS (Polly), STT (Whisper), ELAI avatars |
| 4 | `encoach_ai_generation` | `generation.py` | Exam content generation per module |
| 5 | `encoach_ai_grading` | `grading.py` | Writing/speaking AI grading |
| 6 | `encoach_exam` | `exams.py` | Exam CRUD, approval workflows |
| 7 | `encoach_assignment` | `assignments.py` | Assignment management |
| 8 | `encoach_evaluation` | `evaluations.py` | Evaluation results and AI jobs |
| 9 | `encoach_stats` | `sessions.py`, `stats.py` | Exam sessions and statistics |
| 10 | `encoach_training` | `training.py` | Training content, tips (FAISS), walkthroughs |
| 11 | `encoach_classroom` | `classrooms.py` | Classroom groups |
| 12 | `encoach_subscription` | `subscriptions.py`, `discounts.py` | Packages, payments, discounts |
| 13 | `encoach_registration` | `registration.py` | User registration, batch import |
| 14 | `encoach_ticket` | `tickets.py` | Support tickets |
| 15 | `encoach_api` | `auth.py`, `users.py`, `entities.py`, `storage.py`, `approvals.py` | REST API controllers (81+ endpoints) |

### 1.2 Existing Endpoint Count by Group

| Group | Estimated Count | Status |
|-------|----------------|--------|
| Auth (`/api/login`, `/api/logout`, etc.) | 4 | Built |
| Users (`/api/user`, `/api/users/*`) | 6 | Built |
| Entities & Roles (`/api/entities`, `/api/roles`, `/api/permissions`) | 7 | Built |
| Exams (`/api/exam/*`) | 7 | Built |
| AI Generation (`/api/exam/*/generate`) | 12 | Built |
| Media (`/api/exam/*/media`, `/api/transcribe`) | 6 | Built |
| Evaluations (`/api/evaluate/*`, `/api/grading/*`) | 6 | Built |
| Classrooms (`/api/groups`) | 3 | Built |
| Assignments (`/api/assignments`) | 8 | Built |
| Sessions & Stats (`/api/sessions`, `/api/stats`, `/api/statistical`) | 5 | Built |
| Training (`/api/training/*`) | 4 | Built |
| Subscriptions (`/api/packages`, `/api/stripe`, etc.) | 7 | Built |
| Registration (`/api/register`, `/api/code/*`) | 4 | Built |
| Tickets (`/api/tickets`) | 3 | Built |
| Storage (`/api/storage`) | 2 | Built |
| Approvals (`/api/approval-workflows`) | 3 | Built |
| **TOTAL EXISTING** | **~87** | |

---

## 2. What Needs to Be Built or Modified

### 2.1 New Endpoints Required (Frontend Expects These)

The frontend calls the following endpoints that **do not exist yet**. You must create them.

| Group | Count | Priority |
|-------|-------|----------|
| Taxonomy (subjects, domains, topics) | 14 | P0 |
| Adaptive Learning (diagnostic, proficiency, learning plans) | 16 | P0 |
| Resources (upload, manage, review) | 7 | P1 |
| AI Coaching (chat, suggest, explain, tips) | 6 | P1 |
| AI Utilities (search, insights, alerts, reports) | 5 | P2 |
| Analytics (student, class, subject, content gaps) | 4 | P2 |
| LMS Bridge (courses, batches, timetable, attendance, grades) | 13 | P1 |
| **TOTAL NEW** | **~65** | |

### 2.2 Existing Modules Requiring Modification

| Module | What to Change |
|--------|---------------|
| `encoach_exam` | Add `subject_id` (Many2one to `encoach.subject`) and `topic_ids` (Many2many to `encoach.topic`) fields to `encoach.exam` model |
| `encoach_stats` | Add `topic_id` and `subject_id` fields to `encoach.stat` model |
| `encoach_training` | Add `subject_id` field to `encoach.training.tip` for per-subject FAISS indices |
| `encoach_ai_generation` | Extend prompts to support Math (include LaTeX/KaTeX formatting) and IT (code blocks, syntax) question types |
| `encoach_ai_grading` | Add grading logic for numerical-tolerance (Math) and keyword/pattern (IT) answers |
| `encoach_subscription` | Add `subjects` field to `encoach.package` for subject-scoped subscriptions |
| `encoach_api` | Add new controller files for all new endpoint groups |

---

## 3. New Odoo Modules

Create these 8 new modules:

### 3.1 `encoach_taxonomy`

**Purpose:** Subject/domain/topic/learning-objective hierarchy.

**Models:**

| Model | Key Fields |
|-------|-----------|
| `encoach.subject` | `name` (Char), `code` (Char unique), `is_active` (Boolean), `diagnostic_config` (Text/JSON), `mastery_threshold` (Float default=80), `grading_scale` (Selection: percentage/band/letter), `grading_scale_config` (Text/JSON) |
| `encoach.domain` | `name` (Char), `code` (Char), `subject_id` (M2O encoach.subject), `sequence` (Integer) |
| `encoach.topic` | `name` (Char), `code` (Char), `domain_id` (M2O encoach.domain), `estimated_hours` (Float), `difficulty_level` (Selection: easy/medium/hard), `prerequisite_ids` (M2M self-ref), `question_type_weights` (Text/JSON) |
| `encoach.learning.objective` | `name` (Char), `topic_id` (M2O encoach.topic), `bloom_level` (Selection: remember/understand/apply/analyze/evaluate/create), `sequence` (Integer) |

**Computed fields on `encoach.subject`:** `domain_count`, `topic_count`
**Computed fields on `encoach.domain`:** `topic_count`
**Computed fields on `encoach.topic`:** `objective_count`, `prerequisite_names` (list of names)

### 3.2 `encoach_resources`

**Purpose:** Human-uploaded learning materials tagged to topics.

**Models:**

| Model | Key Fields |
|-------|-----------|
| `encoach.resource` | `name` (Char), `resource_type` (Selection: pdf/video/link/document/interactive), `url` (Char), `file` (Binary), `file_name` (Char), `topic_ids` (M2M encoach.topic), `author_id` (M2O res.users), `is_published` (Boolean), `review_status` (Selection: pending/approved/rejected) |
| `encoach.resource.completion` | `student_id` (M2O res.users), `resource_id` (M2O encoach.resource), `viewed` (Boolean), `time_spent_minutes` (Integer), `rating` (Integer 1-5) |

### 3.3 `encoach_adaptive`

**Purpose:** Core adaptive learning engine -- proficiency tracking, learning plans, content cache.

**Models:**

| Model | Key Fields |
|-------|-----------|
| `encoach.proficiency` | `student_id` (M2O res.users), `topic_id` (M2O encoach.topic), `mastery` (Float 0-100), `mastery_level` (Selection: not_started/beginner/developing/proficient/mastered -- computed from mastery), `last_assessed` (Datetime), `time_spent_minutes` (Integer) |
| `encoach.learning.plan` | `student_id` (M2O res.users), `subject_id` (M2O encoach.subject), `status` (Selection: active/paused/completed), `ai_summary` (Text), `overall_progress` (Float -- computed), `target_completion` (Date) |
| `encoach.learning.plan.item` | `plan_id` (M2O encoach.learning.plan), `topic_id` (M2O encoach.topic), `sequence` (Integer), `status` (Selection: locked/available/in_progress/completed), `estimated_hours` (Float), `actual_hours` (Float), `mastery` (Float -- from proficiency) |
| `encoach.ai.content.cache` | `topic_id` (M2O encoach.topic), `content_type` (Char), `difficulty_level` (Char), `content` (Text/JSON), `model_used` (Char), `review_status` (Selection: auto/reviewed/rejected) |

**Business logic:**
- `mastery_level` is computed: 0-19 = not_started, 20-39 = beginner, 40-59 = developing, 60-79 = proficient, 80-100 = mastered
- `overall_progress` is computed from item statuses (completed_count / total_count * 100)
- When a plan item is completed, unlock the next item(s) based on prerequisite graph

### 3.4 `encoach_adaptive_api`

**Purpose:** REST controllers for the adaptive learning engine.

**Controller files to create:**
- `taxonomy.py` -- `/api/subjects`, `/api/domains`, `/api/topics`, `/api/subjects/{id}/taxonomy`
- `resources.py` -- `/api/resources`
- `diagnostic.py` -- `/api/diagnostic/start`, `/api/diagnostic/answer`, `/api/diagnostic/{id}/result`
- `proficiency.py` -- `/api/proficiency`, `/api/proficiency/summary`, `/api/proficiency/class`
- `learning_plan.py` -- `/api/learning-plan`, `/api/learning-plan/generate`, etc.
- `content.py` -- `/api/topics/{id}/content`, `/api/topics/{id}/practice`, `/api/topics/{id}/mastery-quiz`

### 3.5 `encoach_adaptive_ai`

**Purpose:** AI logic for diagnostics, plan generation, content generation, coaching.

**Key services:**
- Diagnostic question selection (adaptive difficulty algorithm)
- Learning plan generation (topological sort of topics by prerequisites, weighted by proficiency gaps)
- AI content generation for topics (call OpenAI GPT-4o with topic context)
- Practice question generation per topic
- Mastery quiz generation and grading
- Coaching: chat, hints, explanations, study suggestions, writing help, contextual tips

### 3.6 `encoach_lms_api`

**Purpose:** REST API bridge exposing OpenEduCat models to the frontend.

**Controller files to create:**
- `courses.py` -- `/api/courses`, `/api/courses/{id}`, `/api/courses/ai-generate`
- `batches.py` -- `/api/batches`, `/api/batches/{id}`
- `timetable.py` -- `/api/timetable`
- `attendance.py` -- `/api/attendance`
- `grades.py` -- `/api/grades`

**Note:** These controllers wrap the existing OpenEduCat Odoo models (`op.course`, `op.batch`, `op.session`, `op.attendance.sheet`, etc.) behind a clean REST API. Do NOT duplicate the models -- use the OpenEduCat models directly.

### 3.7 `encoach_sis`

**Purpose:** UTAS Student Information System integration.

**Models:**
- `encoach.sis.sync` -- sync job tracking (status, last_run, next_run, error_log)
- `encoach.sis.mapping` -- field mapping between SIS fields and EnCoach fields

### 3.8 `encoach_branding`

**Purpose:** Tenant whitelabeling.

**Models:**
- `encoach.branding` -- `entity_id` (M2O encoach.entity), `logo` (Binary), `primary_color` (Char), `secondary_color` (Char), `font_family` (Char)

---

## 4. Existing Module Modifications

### 4.1 `encoach_exam` -- Add Subject/Topic Fields

```python
# In encoach_exam/models/exam.py, add:
subject_id = fields.Many2one('encoach.subject', string='Subject')
topic_ids = fields.Many2many('encoach.topic', string='Topics')
is_diagnostic = fields.Boolean(string='Is Diagnostic Exam', default=False)
```

### 4.2 `encoach_stats` -- Add Subject/Topic Fields

```python
# In encoach_stats/models/stat.py, add:
subject_id = fields.Many2one('encoach.subject', string='Subject')
topic_id = fields.Many2one('encoach.topic', string='Topic')
```

### 4.3 `encoach_training` -- Add Subject Field

```python
# In encoach_training/models/training_tip.py, add:
subject_id = fields.Many2one('encoach.subject', string='Subject')
```

### 4.4 `encoach_subscription` -- Add Subject Scoping

```python
# In encoach_subscription/models/package.py, add:
subject_ids = fields.Many2many('encoach.subject', string='Included Subjects')
```

### 4.5 `encoach_ai_generation` -- Multi-Subject Prompts

Extend the generation prompts to handle:
- **Math:** Include LaTeX formatting instructions in prompts, numerical-tolerance answer checking
- **IT:** Include code block formatting, syntax highlighting hints, code-completion question types
- **Generic:** Accept `subject_id` and `topic_id` parameters, adjust prompt templates per subject

### 4.6 `encoach_ai_grading` -- Multi-Subject Grading

Extend grading logic:
- **IELTS:** Band scoring (existing)
- **Math:** Numerical tolerance (accept answers within +/- epsilon), step-by-step partial credit
- **IT:** Keyword matching, regex pattern matching, AI-graded code explanations

---

## 5. Complete API Contract

This is the definitive list of every endpoint the frontend calls. Each entry shows the HTTP method, path, request body (if POST/PATCH), and expected response shape. **The TypeScript type files in `encoach_frontend_new/src/types/` are the canonical response shapes.**

### 5.1 Authentication (Existing -- `auth.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `POST` | `/api/login` | `{ login: string, password: string }` | `{ token: string, user: User }` |
| `POST` | `/api/logout` | -- | `void` |
| `GET` | `/api/user` | -- | `User` |
| `POST` | `/api/reset/sendVerification` | `{ email: string }` | `{ success: boolean }` |

**User shape:** See `src/types/auth.ts` -- must include: `id`, `name`, `email`, `login`, `user_type` (one of: student/teacher/admin/corporate/mastercorporate/agent/developer), `is_verified`, `entities` (array of {id, name, role}), `classrooms`.

### 5.2 Users (Existing -- `users.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `GET` | `/api/users/list` | Query: `type`, `entity_id`, `page`, `size`, `search`, `sort`, `order` | `PaginatedResponse<User>` |
| `GET` | `/api/users/{id}` | -- | `User` |
| `PATCH` | `/api/users/update` | `{ id, ...fields }` | `User` |
| `POST` | `/api/users/create` | `Partial<User>` | `User` |
| `POST` | `/api/batch_users` | `{ users: Partial<User>[] }` | `{ success: boolean }` |

**PaginatedResponse shape:** `{ items: T[], total: number, page: number, size: number, pages: number }`

### 5.3 Entities (Existing -- `entities.service.ts`)

| Method | Path | Response |
|--------|------|----------|
| `GET` | `/api/entities` | `PaginatedResponse<Entity>` |
| `GET` | `/api/entities/{id}` | `Entity` |
| `POST` | `/api/entities` | `Entity` |
| `PATCH` | `/api/entities/{id}` | `Entity` |
| `DELETE` | `/api/entities/{id}` | `{ success: boolean }` |
| `GET` | `/api/entities/{id}/roles` | `EntityRole[]` |
| `POST` | `/api/entities/{id}/roles` | `EntityRole` |
| `PATCH` | `/api/roles/{id}/permissions` | `EntityRole` |
| `GET` | `/api/permissions?entity_id=` | `string[]` (list of permission keys for current user) |

### 5.4 Exams (Existing -- `exams.service.ts`)

| Method | Path | Response |
|--------|------|----------|
| `GET` | `/api/exam/{module}` | `PaginatedResponse<Exam>` |
| `GET` | `/api/exam/{module}/{id}` | `Exam` |
| `POST` | `/api/exam` | `Exam` |
| `PATCH` | `/api/exam/{id}` | `Exam` |
| `DELETE` | `/api/exam/{id}` | `{ success: boolean }` |
| `PATCH` | `/api/exam/{id}/access` | `Exam` |
| `GET` | `/api/rubrics` | `PaginatedResponse<Rubric>` |
| `POST` | `/api/rubrics` | `Rubric` |
| `GET` | `/api/rubric-groups` | `PaginatedResponse<RubricGroup>` |
| `GET` | `/api/exam-structures` | `PaginatedResponse<ExamStructure>` |
| `POST` | `/api/exam-structures` | `ExamStructure` |
| `DELETE` | `/api/exam-structures/{id}` | `{ success: boolean }` |
| `GET` | `/api/exam/avatars` | `[{ id, name, thumbnail, voice }]` |

**Note:** `Exam` response must now include optional `subject_id` and `topic_ids` fields.

### 5.5 Assignments (Existing -- `assignments.service.ts`)

| Method | Path | Response |
|--------|------|----------|
| `GET` | `/api/assignments` | `PaginatedResponse<Assignment>` |
| `GET` | `/api/assignments/{id}` | `Assignment` |
| `POST` | `/api/assignments` | `Assignment` |
| `PATCH` | `/api/assignments/{id}` | `Assignment` |
| `DELETE` | `/api/assignments/{id}` | `{ success: boolean }` |
| `POST` | `/api/assignments/{id}/archive` | `Assignment` |
| `POST` | `/api/assignments/{id}/start` | `Assignment` |

### 5.6 Classrooms (Existing -- `classrooms.service.ts`)

| Method | Path | Response |
|--------|------|----------|
| `GET` | `/api/groups` | `PaginatedResponse<Classroom>` |
| `GET` | `/api/groups/{id}` | `Classroom` |
| `POST` | `/api/groups` | `Classroom` |
| `DELETE` | `/api/groups/{id}` | `{ success: boolean }` |
| `POST` | `/api/groups/transfer` | `{ success: boolean }` |
| `POST` | `/api/groups/{id}/members` | `{ success: boolean }` |
| `POST` | `/api/groups/{id}/members/remove` | `{ success: boolean }` |

### 5.7 Stats (Existing -- `stats.service.ts`)

| Method | Path | Response |
|--------|------|----------|
| `GET` | `/api/sessions` | `ExamSession[]` |
| `GET` | `/api/stats` | `ExamStat[]` |
| `GET` | `/api/statistical` | `StatisticalData` |
| `GET` | `/api/stats/performance` | `unknown[]` |

### 5.8 Evaluations (Existing -- `evaluations.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `POST` | `/api/evaluate/writing` | `{ session_id, text, rubric_id? }` | `Evaluation` |
| `POST` | `/api/evaluate/speaking` | `{ session_id, audio_url, rubric_id? }` | `Evaluation` |
| `POST` | `/api/grading/multiple` | `{ session_id, answers: [{exercise_index, answer}] }` | `{ results: [{exercise_index, correct, score}] }` |
| `POST` | `/api/transcribe` | `FormData (audio file)` | `{ text: string }` |

### 5.9 Generation (Existing -- `generation.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `POST` | `/api/exam/{module}/generate` | `{ title, label?, entity_id?, subject_id?, topic_id?, difficulty?, count? }` | `{ exam_id, exercises: [] }` |
| `POST` | `/api/exam/{module}/generate/scratch` | Same as above | Same as above |

**Note:** Must now accept `subject_id` and `topic_id` to scope generation to specific subjects/topics.

### 5.10 Training, Subscriptions, Tickets, Storage, Approvals (Existing)

These follow the existing patterns. See `training.service.ts`, `subscriptions.service.ts`, `tickets.service.ts`, `storage.service.ts`, `approvals.service.ts` for exact paths.

---

### 5.11 Taxonomy (NEW -- `taxonomy.service.ts`)

| Method | Path | Request/Params | Response |
|--------|------|---------------|----------|
| `GET` | `/api/subjects` | -- | `Subject[]` |
| `GET` | `/api/subjects/{id}` | -- | `Subject` |
| `POST` | `/api/subjects` | `Partial<Subject>` | `Subject` |
| `PATCH` | `/api/subjects/{id}` | `Partial<Subject>` | `Subject` |
| `DELETE` | `/api/subjects/{id}` | -- | `{ success: boolean }` |
| `GET` | `/api/subjects/{id}/taxonomy` | -- | `TaxonomyTree` (nested: subject + domains + topics + objectives) |
| `POST` | `/api/subjects/{id}/taxonomy/import` | `FormData (CSV/JSON)` | `{ success: boolean }` |
| `GET` | `/api/domains` | Query: `subject_id?` | `Domain[]` |
| `POST` | `/api/domains` | `Partial<Domain>` | `Domain` |
| `PATCH` | `/api/domains/{id}` | `Partial<Domain>` | `Domain` |
| `DELETE` | `/api/domains/{id}` | -- | `{ success: boolean }` |
| `POST` | `/api/domains/{id}/ai-suggest` | -- | `{ suggestions: Partial<Topic>[] }` |
| `GET` | `/api/topics` | Query: `domain_id?`, `subject_id?` | `Topic[]` |
| `POST` | `/api/topics` | `Partial<Topic>` | `Topic` |
| `PATCH` | `/api/topics/{id}` | `Partial<Topic>` | `Topic` |
| `DELETE` | `/api/topics/{id}` | -- | `{ success: boolean }` |

**TaxonomyTree shape:**
```json
{
  "subject": { "id": 1, "name": "IELTS", "code": "IELTS", ... },
  "domains": [
    {
      "id": 1, "name": "Writing", "code": "W", "sequence": 1,
      "topics": [
        {
          "id": 1, "name": "Task 1", "difficulty_level": "medium",
          "objectives": [
            { "id": 1, "name": "Describe trends", "bloom_level": "apply" }
          ]
        }
      ]
    }
  ]
}
```

### 5.12 Resources (NEW -- `resources.service.ts`)

| Method | Path | Request/Params | Response |
|--------|------|---------------|----------|
| `GET` | `/api/resources` | Query: `topic_id?`, `resource_type?`, `review_status?`, `page`, `size`, `search` | `PaginatedResponse<Resource>` |
| `POST` | `/api/resources` | `FormData` (file + metadata) | `Resource` |
| `PATCH` | `/api/resources/{id}` | `Partial<Resource>` | `Resource` |
| `DELETE` | `/api/resources/{id}` | -- | `{ success: boolean }` |
| `POST` | `/api/resources/{id}/complete` | -- | `ResourceCompletion` |
| `POST` | `/api/resources/{id}/rate` | `{ rating: number }` | `{ success: boolean }` |
| `GET` | `/api/resources/{id}/download` | -- | Binary file (stream) |

### 5.13 Diagnostic Assessment (NEW -- `adaptive.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `POST` | `/api/diagnostic/start` | `{ subject_id: number }` | `{ session: DiagnosticSession, first_question: DiagnosticQuestion }` |
| `POST` | `/api/diagnostic/answer` | `{ session_id, question_id, answer }` | `{ next_question?: DiagnosticQuestion, completed: boolean }` |
| `GET` | `/api/diagnostic/{id}/result` | -- | `DiagnosticResult` |

**DiagnosticQuestion shape:**
```json
{
  "id": "q_uuid",
  "topic_id": 5,
  "topic_name": "Line Graphs",
  "domain_name": "Writing",
  "difficulty": "medium",
  "question_type": "multiple_choice",
  "question_text": "Which of the following...",
  "options": ["A", "B", "C", "D"],
  "time_limit_seconds": 120
}
```

**Diagnostic algorithm:**
1. On `/start`: Create a diagnostic session. Pull topics from all domains. Start with `starting_difficulty` from subject's `diagnostic_config`.
2. On `/answer`: Grade the answer. If correct, increase difficulty and mastery estimate for that topic. If incorrect, decrease. Select the next question from a different domain/topic. Use Computer Adaptive Testing (CAT) principles.
3. Continue until `total_question_cap` reached or all domains have 2+ data points.
4. On completion: Write proficiency records for all assessed topics.

### 5.14 Proficiency (NEW -- `adaptive.service.ts`)

| Method | Path | Params | Response |
|--------|------|--------|----------|
| `GET` | `/api/proficiency` | Query: `subject_id?` | `Proficiency[]` |
| `GET` | `/api/proficiency/summary` | -- | `ProficiencySummary[]` |
| `GET` | `/api/proficiency/class` | Query: `subject_id?` | Class-level aggregation |

**ProficiencySummary shape:**
```json
{
  "subject_id": 1,
  "subject_name": "IELTS",
  "overall_mastery": 65.5,
  "domain_scores": [{ "domain_id": 1, "domain_name": "Writing", "mastery": 72 }],
  "topics_mastered": 8,
  "topics_total": 20
}
```

### 5.15 Learning Plan (NEW -- `adaptive.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `GET` | `/api/learning-plan` | Query: `subject_id` | `LearningPlan` |
| `POST` | `/api/learning-plan/generate` | `{ subject_id, target_completion? }` | `LearningPlan` |
| `PATCH` | `/api/learning-plan/{id}` | `Partial<LearningPlan>` | `LearningPlan` |
| `POST` | `/api/learning-plan/{id}/pause` | -- | `LearningPlan` |
| `POST` | `/api/learning-plan/{id}/resume` | -- | `LearningPlan` |

**Plan generation algorithm:**
1. Get student's proficiency records for the subject.
2. Identify topics below mastery threshold.
3. Topologically sort topics by prerequisites.
4. Create plan items in sequence. First item = available, rest = locked.
5. Call GPT-4o to generate `ai_summary` (natural language description of the plan).
6. Set `estimated_hours` from topic definitions.

### 5.16 Content Delivery (NEW -- `adaptive.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `GET` | `/api/topics/{id}/content` | -- | `TopicContent` |
| `POST` | `/api/topics/{id}/generate-content` | -- | `TopicContent` |
| `POST` | `/api/topics/{id}/practice` | -- | `{ questions: [] }` |
| `POST` | `/api/topics/{id}/practice/grade` | `{ answers: [] }` | `{ results: [], mastery_update: number }` |
| `POST` | `/api/topics/{id}/mastery-quiz` | -- | `{ questions: [] }` |
| `POST` | `/api/topics/{id}/mastery-quiz/submit` | `{ answers: [] }` | `{ passed: boolean, score: number, mastery_update: number }` |

**TopicContent shape:**
```json
{
  "topic_id": 5,
  "topic_name": "Line Graphs",
  "resources": [{ "id": 1, "name": "...", "resource_type": "pdf", "url": "..." }],
  "ai_content": {
    "explanation": "Line graphs show...",
    "examples": ["Example 1...", "Example 2..."],
    "key_points": ["Point 1", "Point 2"],
    "model_used": "gpt-4o"
  },
  "has_content_gap": true
}
```

**`has_content_gap`** = true when no human-uploaded resources exist for this topic, and AI content was generated to fill the gap.

### 5.17 AI Coaching (NEW -- `coaching.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `POST` | `/api/coach/chat` | `{ message, context?: { page?, topic_id?, subject_id? }, history?: AiChatMessage[] }` | `{ message: string, suggestions?: string[] }` |
| `POST` | `/api/coach/hint` | `{ topic_id, question_id }` | `{ hint: string }` |
| `POST` | `/api/coach/explain` | `{ context, scores? }` | `{ explanation: string }` |
| `POST` | `/api/coach/suggest` | `{ subject_id? }` | `{ suggestions: string[], study_plan_tips: string[] }` |
| `POST` | `/api/coach/writing-help` | `{ text, task_type }` | `{ feedback, improved, grammar_notes: string[] }` |
| `GET` | `/api/coach/tip` | Query: `context` | `{ title, content, category }` |

All coaching endpoints use GPT-4o with appropriate system prompts.

### 5.18 AI Utilities (NEW -- `analytics.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `POST` | `/api/ai/search` | `{ query }` | `AiSearchResult[]` |
| `POST` | `/api/ai/insights` | `{ data: {} }` | `AiInsight[]` |
| `GET` | `/api/ai/alerts` | -- | `AiAlert[]` |
| `POST` | `/api/ai/report-narrative` | `{ report_type, data: {} }` | `{ narrative: string }` |
| `POST` | `/api/ai/batch-optimize` | `{ batch_id }` | `AiBatchOptimization[]` |
| `POST` | `/api/ai/grade-suggest` | `{ submission_id, text, rubric_id? }` | `AiGradingResult` |

### 5.19 Analytics (NEW -- `analytics.service.ts`)

| Method | Path | Params | Response |
|--------|------|--------|----------|
| `GET` | `/api/analytics/student` | Query params | Dashboard data |
| `GET` | `/api/analytics/class` | Query params | Class analytics |
| `GET` | `/api/analytics/subject` | Query params | Subject analytics |
| `GET` | `/api/analytics/content-gaps` | Query: `subject_id?` | `{ gaps: [{ topic_id, topic_name, resource_count }] }` |

### 5.20 LMS Bridge (NEW -- `lms.service.ts`)

| Method | Path | Request | Response |
|--------|------|---------|----------|
| `GET` | `/api/courses` | Query: `page`, `size`, `search`, `status` | `PaginatedResponse<Course>` |
| `GET` | `/api/courses/{id}` | -- | `Course` |
| `POST` | `/api/courses` | `CourseCreateRequest` | `Course` |
| `PATCH` | `/api/courses/{id}` | `Partial<CourseCreateRequest>` | `Course` |
| `DELETE` | `/api/courses/{id}` | -- | `{ success: boolean }` |
| `POST` | `/api/courses/ai-generate` | `{ title, subject_id?, level? }` | `{ outline: {} }` |
| `GET` | `/api/batches` | Query: pagination | `PaginatedResponse<Batch>` |
| `GET` | `/api/batches/{id}` | -- | `Batch` |
| `POST` | `/api/batches` | `Partial<Batch>` | `Batch` |
| `PATCH` | `/api/batches/{id}` | `Partial<Batch>` | `Batch` |
| `DELETE` | `/api/batches/{id}` | -- | `{ success: boolean }` |
| `GET` | `/api/timetable` | Query: `course_id?`, `teacher_id?`, `batch_id?` | `TimetableSession[]` |
| `POST` | `/api/timetable` | `Partial<TimetableSession>` | `TimetableSession` |
| `GET` | `/api/attendance` | Query: `course_id?`, `student_id?`, `date?` | `AttendanceRecord[]` |
| `POST` | `/api/attendance` | `{ course_id, date, records: [{student_id, status}] }` | `{ success: boolean }` |
| `GET` | `/api/grades` | Query: `course_id?`, `student_id?` | `GradeRecord[]` |

**Course shape:** See `src/types/lms.ts` -- must include: `id`, `title`, `code`, `subject_id?`, `subject_name?`, `instructor_id`, `instructor_name`, `description`, `level`, `modules[]`, `enrolled`, `max_capacity`, `status`, `start_date`, `end_date`, `progress?`.

---

## 6. Data Model Specification

All data model details are in **Section 30 of `ENCOACH_UNIFIED_SRS.md`** (existing models: 30.1, new models: 30.2-30.4). Refer to that document for the complete field-level specification.

**Key relationships:**
```
Subject (1) → (N) Domain (1) → (N) Topic (1) → (N) LearningObjective
Topic (M) ↔ (M) Topic (prerequisites -- self-referencing M2M)
Topic (M) ↔ (M) Resource
Student (1) → (N) Proficiency (per topic)
Student (1) → (N) LearningPlan (per subject) → (N) LearningPlanItem (per topic)
Exam → Subject (optional M2O)
Exam → Topic[] (optional M2M)
```

---

## 7. AI Service Integration

The following external AI services are already configured (API keys in `encoach_core/data/constants.xml` and server `.env`):

| Service | Purpose | Config Key |
|---------|---------|-----------|
| OpenAI GPT-4o | Content generation, grading, coaching, plan generation | `encoach.openai_api_key` |
| OpenAI GPT-3.5-turbo | Lightweight tasks (tips, search, suggestions) | Same key |
| OpenAI Whisper (local) | Speech-to-text | Local model |
| AWS Polly (Neural) | Text-to-speech for listening exams | `encoach.aws_access_key_id`, `encoach.aws_secret_access_key` |
| ELAI | AI avatar video generation | `encoach.elai_api_key` |
| GPTZero | AI writing detection | `encoach.gptzero_api_key` |
| FAISS + SentenceTransformers | Semantic similarity for training tips | Local model (`all-MiniLM-L6-v2`) |

**New AI tasks to implement:**

| Task | Model | Endpoint |
|------|-------|----------|
| Diagnostic question generation | GPT-4o | `/api/diagnostic/start`, `/answer` |
| Learning plan generation | GPT-4o | `/api/learning-plan/generate` |
| Topic content generation | GPT-4o | `/api/topics/{id}/generate-content` |
| Practice question generation | GPT-4o | `/api/topics/{id}/practice` |
| Mastery quiz generation | GPT-4o | `/api/topics/{id}/mastery-quiz` |
| AI coaching chat | GPT-4o | `/api/coach/chat` |
| Study suggestions | GPT-3.5-turbo | `/api/coach/suggest` |
| Writing feedback | GPT-4o | `/api/coach/writing-help` |
| Grade explanation | GPT-4o | `/api/coach/explain` |
| Contextual tips | GPT-3.5-turbo | `/api/coach/tip` |
| Semantic search | FAISS | `/api/ai/search` |
| Report narrative | GPT-4o | `/api/ai/report-narrative` |
| Data insights | GPT-4o | `/api/ai/insights` |
| Topic suggestion | GPT-4o | `/api/domains/{id}/ai-suggest` |
| Course outline generation | GPT-4o | `/api/courses/ai-generate` |
| Batch optimization | GPT-3.5-turbo | `/api/ai/batch-optimize` |

---

## 8. Response Format Standards

### 8.1 Paginated Responses

All list endpoints that support pagination must return:

```json
{
  "items": [...],
  "total": 150,
  "page": 1,
  "size": 20,
  "pages": 8
}
```

Query parameters: `page` (1-based), `size` (default 20), `search` (text filter), `sort` (field name), `order` (asc/desc).

### 8.2 Error Responses

```json
{
  "error": "Human-readable error message",
  "code": "ERROR_CODE",
  "details": {}
}
```

HTTP status codes: 400 (validation), 401 (unauthorized), 403 (forbidden), 404 (not found), 500 (server error).

### 8.3 Success Responses

For write operations that don't return an entity:

```json
{
  "success": true,
  "message": "Optional message"
}
```

### 8.4 JWT Token

- Token returned on login as `{ token: "...", user: {...} }`
- Frontend sends `Authorization: Bearer <token>` on every request
- 401 response = token expired, frontend redirects to login

---

## 9. Non-Functional Requirements

| Requirement | Specification |
|-------------|--------------|
| API response time (CRUD) | < 2 seconds |
| Diagnostic question generation | < 3 seconds |
| AI grading (writing/speaking) | < 60 seconds |
| Learning plan generation | < 10 seconds |
| AI content generation | < 15 seconds |
| Concurrent students per subject | 200 |
| Topics per subject | 500 |
| Resources per subject | 1,000 |
| Pagination on all list endpoints | Required |
| Server-side search/filter | Required |
| Soft delete for resources | Required (preserve completion records) |
| Proficiency weighted averages | Required (never direct overwrite) |
| Math content | LaTeX/KaTeX formatting in question text |
| IT content | Code blocks with language hints in question text |

---

## 10. Implementation Priority

| Phase | Modules | Endpoints | Estimated Effort |
|-------|---------|-----------|-----------------|
| **P0** | `encoach_taxonomy`, modify `encoach_exam`/`encoach_stats`/`encoach_training` | Taxonomy CRUD (14), Subject/Topic fields on existing models | 1 week |
| **P1** | `encoach_adaptive`, `encoach_adaptive_api`, `encoach_adaptive_ai` | Diagnostic (3), Proficiency (3), Learning Plan (5), Content (6) | 2 weeks |
| **P1** | `encoach_resources` | Resource CRUD (7) | 3 days |
| **P1** | `encoach_lms_api` | LMS bridge (13) | 1 week |
| **P2** | `encoach_adaptive_ai` (coaching) | AI Coaching (6), AI Utilities (5) | 1 week |
| **P2** | Analytics controllers | Analytics (4) | 3 days |
| **P3** | `encoach_sis`, `encoach_branding` | SIS (4), Branding | 1 week |
| **P3** | Multi-subject prompts/grading | Modify generation & grading modules | 3 days |
| **TOTAL** | 8 new modules + 7 modified | ~65 new endpoints | ~7 weeks |

---

## Appendix: File Handoff Checklist

| File/Folder | Purpose | Location |
|-------------|---------|----------|
| This document | Backend SRS | `ODOO_BACKEND_SRS_v3.md` |
| Product SRS | Full platform specification | `ENCOACH_UNIFIED_SRS.md` |
| TypeScript types | Response shape contracts | `encoach_frontend_new/src/types/*.ts` |
| Service layer | Endpoint path contracts | `encoach_frontend_new/src/services/*.ts` |
| Existing modules | Already-built backend | `ielts-be-v0/` |
| Developer feedback | Review of existing work | `ielts-be-v0/DEVELOPER_FEEDBACK.md` |
| AI keys & config | Service credentials | `ielts-be-v0/encoach_core/data/constants.xml` |

---

*This document supersedes `ODOO_MIGRATION_SRS_v2.md`. The Odoo developer should treat this as the definitive backend specification. All endpoint paths, response shapes, and data models are aligned with the production frontend at `encoach_frontend_new/`.*
