Addresses the QA notes on the end-to-end approval flow. Highlights:
Backend
- encoach_ai/generation_submit: create encoach.approval.request on submit so
submitted exams actually reach the approver queue (previously only the
workflow id was stored on the exam, leaving approvers with an empty inbox).
Initial exam status flips to pending_approval instead of draft.
- encoach_exam_template/approval_workflows:
* /api/approval-users filters out students (user_type='student',
op_student_id set, share=True) so the approver dropdown only lists staff.
* _ser_request enriches requests with target_name / target_status and the
current stage approver for UI badges.
* list_requests supports mine=1 / requester=1 so approvers only see queue
items awaiting their action.
* approve_request / reject_request now transition the underlying
encoach.exam.custom to published / rejected on final approval.
- encoach.exam.custom.status: add pending_approval and rejected states to
match the approval workflow transitions.
Frontend UX
- GenerationPage: rubric field shows "Auto-graded — no rubric" for Listening
and Reading (rubrics only apply to Writing / Speaking). Divider dropdowns
now carry explicit help text explaining None / Line / Space / Page Break
and where to write prompts. Selecting an official exam structure
auto-populates the required tasks/sections/parts, the delete button is
hidden for essential tasks, and submit is blocked if the user dropped
below the structure's required count.
- RubricsPage: "Add Criterion" is now a dropdown with IELTS-specific
presets (Task Achievement, Coherence & Cohesion, Fluency & Coherence, …)
keyed off the selected skill, plus a "Custom (blank row)" fallback.
- AdminCourses: added tooltips and helper copy clarifying Difficulty vs
CEFR Level in the Create Course dialog.
- CustomExamCreate: validate the whole form before Save/Publish, surface
backend error messages (413/504/401) instead of a generic toast, read the
exam id from the correct response field, and avoid marking Publish as
failed when only the optional Save-as-Template step errors.
- ResourceManager / TeacherLibrary: upload toast now shows a concrete
reason (HTTP 413 / 504 / 401) instead of a generic "Upload failed".
Config (504 / 413 / resource upload fixes)
- odoo.conf + odoo-docker.conf: raise limit_time_cpu to 600s,
limit_time_real to 900s, limit_request to 128 MB, and memory caps so
/api/exam/generation/submit and multipart resource uploads stop hitting
504 / 413 during QA.
- frontend/Dockerfile (nginx): add client_max_body_size 128m, bump
proxy_read_timeout / proxy_send_timeout to 900s, and disable request
buffering so large AI/resource payloads stream through the proxy.
Made-with: Cursor
EnCoach — Adaptive AI Learning Platform
EnCoach is a smart learning environment for individual and collaborative learning, fully integrated with AI and equipped with intelligent, professional-grade exercises, assessments, and e-exams.
This repository hosts the full stack:
- Backend — Odoo 19 + ~30
encoach_*modules (Python 3.12, PostgreSQL 16) - Frontend — React 18 + Vite + TypeScript single-page app
- AI layer — OpenAI + pgvector RAG, quality-gate validation, IELTS validator
- Ops — Docker Compose, JWT auth with refresh tokens, Prometheus-compatible metrics, dynamic OpenAPI 3.0 spec
Canonical trees:
backend/(all server code) andfrontend/(all client code). The legacynew_project/directory is deprecated — seenew_project/DEPRECATED.md.
1. Quickstart
Docker (recommended)
docker compose up -d
# Odoo: http://localhost:8069
# Frontend: cd frontend && npm install && npm run dev (http://localhost:8080)
Create a new Odoo database on first visit, then install the encoach_api
module to pull in every encoach_* dependency.
Source
Prerequisites: Python 3.12, PostgreSQL 16, Node 20, npm 10.
# Backend
./setup.sh # creates venv, installs requirements.txt
./run.sh # starts Odoo on :8069
# Frontend
cd frontend
npm install
npm run dev # starts Vite on :8080, proxies /api → :8069
See MANUAL-RUN.md for a step-by-step walkthrough and
CONNECT-POSTGRES.md for database wiring.
2. Repository layout
odoo19/
├── backend/
│ └── custom_addons/
│ ├── encoach_api/ REST base + JWT + OpenAPI + metrics
│ ├── encoach_core/ Users, entities, roles, permissions
│ ├── encoach_taxonomy/ Subject → Domain → Topic
│ ├── encoach_ai/ OpenAI wrapper, cefr_mapper, validator
│ ├── encoach_ai_course/ AI course/exam generation pipelines
│ ├── encoach_ai_grading/ AI grading (writing/speaking/math/IT)
│ ├── encoach_ai_media/ TTS (Polly), STT (Whisper), ELAI
│ ├── encoach_vector/ pgvector store + RAG embeddings
│ ├── encoach_exam_template/ Canonical exam + student attempt models
│ ├── encoach_scoring/ Score computation, CEFR mapping
│ ├── encoach_quality_gate/ Automated content-quality checks
│ ├── encoach_ielts_validation/ IELTS-specific validators
│ ├── encoach_adaptive/ Adaptive engine, style matcher
│ ├── encoach_lms_api/ OpenEduCat bridge + LMS endpoints
│ ├── encoach_branding/ White-label config per entity
│ └── … (other encoach_* modules)
├── frontend/
│ ├── src/
│ │ ├── pages/ Route pages (React.lazy code-split)
│ │ ├── components/ Shared UI (shadcn/ui + custom)
│ │ ├── services/ Thin API wrappers over fetch
│ │ ├── hooks/queries/ React Query hooks + keys
│ │ ├── lib/api-client.ts Fetch + auto JWT refresh
│ │ └── types/ Shared DTO types
│ └── vite.config.ts manualChunks: react, query, charts, radix…
├── docs/
│ ├── PROJECT_SUMMARY.md Release notes & architecture history
│ ├── adr/ Architecture Decision Records
│ └── ENCOACH_*.md SRS, workflows, user stories
├── docker-compose.yml
├── Dockerfile
├── requirements.txt Python deps (pgvector, textstat, etc.)
└── README.md You are here
3. Architecture at a glance
┌────────────────────┐ HTTPS/JWT ┌────────────────────┐
│ React SPA (Vite) │ ───────────────► │ Odoo 19 + FastAPI-style │
│ - React Query │ ◄─────────────── │ controllers (encoach_*) │
│ - next-themes │ X-Request-Id └──────┬─────────────┘
│ - shadcn/ui │ │
└────────────────────┘ ▼
┌──────────────┐
│ PostgreSQL │
│ + pgvector │
└──────┬───────┘
│
▼
┌──────────────┐
│ OpenAI, │
│ Whisper, │
│ Polly… │
└──────────────┘
Every request carries an X-Request-Id and emits structured JSON logs.
Prometheus-compatible counters are exposed at /api/metrics, and the live
OpenAPI 3.0 spec is at /api/openapi.json.
4. Key conventions
- Canonical response envelope — list endpoints return
{ items: T[], total, page, size, data: T[] }(seeencoach_api.controllers.base.paginated_envelope). - CEFR mapping — only
encoach_ai.services.cefr_mapperis canonical. Do not reintroduce localband_to_cefrcopies. - JWT tokens — short-lived access tokens (1h) + revocable refresh tokens
(7d). Only
accesstokens are accepted as Bearer credentials; refresh tokens must go through/api/auth/refresh. Seedocs/adr/0002-jwt-refresh-token-flow.md. - RAG metadata — vector embeddings carry
course_id,subject_id,entity_id,taxonomy,content_hash. Chunking kicks in above 2 000 chars. - Frontend pagination —
PaginatedResponse<T>exposes bothitemsanddata. Read fromitemsin new code. - Frontend theming — tokens live in
frontend/src/index.css(:rootand.dark). Always usehsl(var(--token))instead of raw hex.
5. Health, observability, docs
| Endpoint | Purpose |
|---|---|
GET /api/health |
Liveness (always 200 when server is up) |
GET /api/health/ready |
Readiness (DB + required config) |
GET /api/openapi.json |
Dynamic OpenAPI 3.0 spec generated from @http.route |
GET /api/metrics |
Prometheus-format counters per route |
6. Deployment
Staging and production both use Docker Compose. The staging server rebuilds
automatically from main; never force-push. See
INSTALL-ODOO-SUMMARY.md for bootstrap notes.
| Service | Staging URL |
|---|---|
| Odoo backend | http://5.189.151.117:8069 |
| React frontend | http://5.189.151.117:3000 |
The .env file is never committed. On staging it lives at
/opt/encoach/backend-v2/.env.
7. Further reading
| Document | Description |
|---|---|
docs/PROJECT_SUMMARY.md |
Release notes + architecture history |
docs/adr/ |
Architecture Decision Records (why we built it this way) |
docs/ENCOACH_UNIFIED_SRS.md |
Unified frontend + backend SRS |
docs/ENCOACH_ODOO19_BACKEND_SRS.md |
Backend SRS v3.0 |
docs/ENCOACH_WORKFLOWS_BACKEND_SRS.md |
Backend workflows |
docs/ENCOACH_WORKFLOWS_FRONTEND_SRS.md |
Frontend workflows |
8. Contributing
- Branch from
main— never push direct. Branch protection enforces it. - Run
npx tsc --noEmit -p tsconfig.app.json(frontend) and the module smoke tests before opening a PR. - Every architectural decision should be captured as an ADR under
docs/adr/. Copy0000-template.mdto start one. - Open the PR against
mainand request review from devops (Talal).