feat(generation): rebuild Generation Page with full AI workflows

- Rebuild GenerationPage.tsx from static placeholder to production-parity
  exam generation wizard with all 4 IELTS modules (Reading, Listening,
  Writing, Speaking) plus Level and Industry
- Add per-module config: timer, CEFR difficulty tags, access type,
  entities, approval workflow, rubric, grading system, shuffling
- Reading: AI passage generation, 5 exercise types (MCQ, Fill Blanks,
  Write Blanks, True/False, Paragraph Match), categories/types
- Listening: 4 section types, AI context generation, TTS audio generation
- Writing: Task 1/2, AI instruction generation, word limits, marks
- Speaking: 3 parts, AI script generation, avatar video generation
  with 7 avatar options
- Wire ExamStructuresPage to real CRUD API (list/create/delete)
- Add backend exam_structure model and controller (/api/exam-structures)
- Enhance ai_controller with 5 specialized generation handlers
  (passage, exercises, writing instructions, speaking script,
  listening context)
- Add POST /api/exam/generation/submit for exam creation workflow
- Fix media.service avatar video endpoint alignment
- All 12 API tests passed, browser-verified with real OpenAI calls

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-11 14:21:40 +04:00
parent 907a5c0e92
commit 0c8443256d
6 changed files with 679 additions and 0 deletions

View File

@@ -8,3 +8,4 @@ from . import speaking_card
from . import exam_custom
from . import exam_custom_section
from . import exam_assignment
from . import exam_structure

View File

@@ -0,0 +1,14 @@
from odoo import models, fields
class EncoachExamStructure(models.Model):
_name = 'encoach.exam.structure'
_description = 'Reusable Exam Structure'
_order = 'create_date desc'
name = fields.Char(size=200, required=True)
entity_id = fields.Many2one('encoach.entity', ondelete='set null')
industry = fields.Char(size=100)
modules = fields.Text(help='JSON list of module keys, e.g. ["reading","listening"]')
config = fields.Text(help='JSON config: timer, difficulty, passage counts per module')
active = fields.Boolean(default=True)