v4 #1

Merged
yamen merged 19 commits from v4 into main 2026-04-26 00:38:14 +02:00

19 Commits

Author SHA1 Message Date
Yamen Ahmad
35ccc0dfb2 feat(platform): ship AI fallback stack and entity-scoped course planning
Unifies the new LangGraph-driven course-plan/media flow with robust provider fallbacks, admin AI provider settings, editable book-style materials, and strict entity isolation across LMS/course-plan APIs. Adds admin-only entity membership management in the Entities UI so users can switch linked entities directly from the platform.

Made-with: Cursor
2026-04-26 02:34:52 +04:00
Yamen Ahmad
096b042daf feat(course-plan): RAG sources + multi-modal media + assignments + student view
Builds the §24 product on top of the LangGraph runtime from §22:

Phase A (Sources / RAG)
  - encoach.course.plan.source model (file | url | text)
  - SourceIndexer extracts PDF (pypdf), DOCX (python-docx), HTML, plain
    text and embeds chunks via the existing pgvector pipeline scoped to
    plan_id, so resources.search only returns the plan's own corpus
  - Endpoints: list/create/upload/reindex/delete + plan-scoped retrieval

Phase B (Deliverables)
  - services.deliverables.compute_deliverables walks the plan, derives
    {planned, generated, ready} per week from material + media state
  - GET /api/ai/course-plan/<id>/deliverables drives the new wizard
    preview step and the live progress strip on the detail page

Phase C (Multi-modal media)
  - encoach.course.plan.media model + MediaService:
    audio: AWS Polly (default) or ElevenLabs
    image: OpenAI DALL-E 3, capped per plan via system parameter
    video: local ffmpeg subprocess (image + audio -> MP4 1280x720)
  - Three new agent tools (media.synthesize_audio / generate_image /
    compose_video), wired into course_week_materials and a new
    course_media_director agent
  - Endpoints per material + week-level batch generator

Phase D (Assignments)
  - encoach.course.plan.assignment supports mode='batch' (op.batch) or
    mode='students' (res.users), with due_date + message + state
  - REST endpoints to list / create / delete assignments

Phase E (Student view)
  - /api/student/course-plans + /api/student/course-plans/<id>
    enforce visibility via assignment.expand_user_ids()
  - New /student/course-plans list + read-only drilldown rendering
    audio/image/video tiles from /web/content/<attachment_id>

Cross-cutting
  - encoach.ai.tool.category: + media (so the new tools register)
  - encoach.embedding gains a plan_id filter for plan-scoped RAG
  - Wizard adds Sources + Multimedia steps; AdminCoursePlanDetail
    rewritten with DeliverablesStrip + SourcesCard + AssignmentsCard +
    per-material MediaDrawer
  - ~280 new EN + AR i18n keys (full RTL coverage)
  - smoke_course_plan.py exercises every phase via odoo-bin shell;
    last run: PASS A/B/D/E + DALL-E 3 image (753 KB), Polly audio
    fails cleanly when AWS creds aren't configured (expected)

Documentation: §24 added to docs/PROJECT_SUMMARY.md with phase-by-phase
artefact list, endpoints, smoke test, ops notes, and gotchas.

Made-with: Cursor
2026-04-25 17:13:01 +04:00
Yamen Ahmad
0ed7f88cab fix(security): remove comment from ir.model.access.csv causing import error
Made-with: Cursor
2026-04-25 14:59:27 +04:00
Yamen Ahmad
ed8e75d88c feat(course-plan): GE1-style AI course planning with deliverables, resources, media, assignments
Based on UTAS GE1 Course Outline structure (Reading/Writing 10hrs + Listening/Speaking 8hrs)

New Models:
- encoach.course.plan.deliverable: Explicit learning outcome tracking by week/skill
- encoach.course.plan.resource.dep: Resource dependencies (textbooks, videos, etc.)
- encoach.course.plan.assignment: Assign plans to classes/students with progress tracking
- encoach.course.plan.assignment.deliverable: Per-student deliverable completion status

Extended Models:
- course.plan.material: Added media fields (media_type, media_asset_url, media_asset_id,
  media_generation_prompt, media_metadata_json) for rich content
- New material types: video_lesson, audio_recording, image_visual, interactive, assessment

AI Agent Tools (agent_tools.py):
- deliverables.detect: Parse course outlines (like GE1 PDF) and extract structured outcomes
- deliverables.fetch: Get deliverables for AI to reference when generating
- resources.fetch: Check available resources before generating content
- resources.save: Persist resource dependencies
- media.suggest_visuals: AI suggests images/diagrams for materials
- media.generate_image: Generate educational images (DALL-E integration ready)
- media.generate_audio: Generate TTS audio (ElevenLabs/Polly integration ready)
- assignment.*: Create assignments and track progress

Pipeline Enhancements (course_plan_pipeline.py):
- generate_deliverables_from_outline(): Parse PDF/text outlines into structured deliverables
- generate_week_materials_with_resources(): Resource-aware content generation
- suggest_media_for_material(): AI visual aid suggestions
- generate_media_for_material(): Actual image/audio generation

New AI Agents (agents_defaults.xml):
- deliverable_detector: Parses GE1-style outlines, extracts deliverables week-by-week
- media_generator: Creates images/audio for teaching materials
- Updated course_planner & course_week_materials with resource tools

REST APIs (course_plan.py):
POST /api/ai/course-plan/<id>/deliverables/detect - Parse outline
GET  /api/ai/course-plan/<id>/deliverables - List deliverables
PUT  /api/ai/course-plan/deliverables/<id> - Update status

GET  /api/ai/course-plan/<id>/resources - List resources
POST /api/ai/course-plan/<id>/resources - Add resource

POST /api/ai/course-plan/materials/<id>/media/suggest - Get visual suggestions
POST /api/ai/course-plan/materials/<id>/media/generate - Generate image/audio

POST /api/ai/course-plan/<id>/assignments - Assign to class/student
GET  /api/ai/course-plan/<id>/assignments - List assignments
GET  /api/ai/course-plan/assignments/<id> - Get with progress
PUT  /api/ai/course-plan/assignments/<id>/deliverables/<del_id> - Update status

Security: Added ir.model.access.csv entries for all new models
Made-with: Cursor
2026-04-25 14:57:04 +04:00
Yamen Ahmad
971e9860c8 ci(frontend): add Gitea staging deploy workflow under frontend/ (monorepo mirror of encoach_frontend New_v2 main)
Made-with: Cursor
2026-04-25 11:55:20 +04:00
Yamen Ahmad
8d173b93cb merge(devops): integrate main (staging CI deploy workflow + PR history) 2026-04-25 11:52:28 +04:00
a5a3a2dc62 ci: add auto-deploy workflow to staging 2026-04-25 09:24:33 +02:00
Yamen Ahmad
fa6f4976c3 chore(ops): add scripts/backup-db.sh — Odoo-format DB backup
Produces an Odoo-compatible <db>.zip restorable via /web/database/manager
on the live VPS (or curl -F backup_file=@... /web/database/restore).

Why this script instead of POST /web/database/backup:
the Odoo HTTP backup endpoint shells out to pg_dump from $PATH and on
this host that returns "Command pg_dump not found" because pg_dump
lives in the conda env that Odoo wasn't launched from. This script
calls pg_dump directly with PGBIN, copies the filestore, builds a
correct manifest.json (odoo_dump, db_name, version, pg_version,
modules), and zips the three artefacts in the exact layout Odoo's
restore code expects.

Layout matches Odoo's own backup output:
  <db>.zip
  |- dump.sql       (pg_dump --no-owner --format=p)
  |- manifest.json  (odoo_dump=1, version, pg_version, installed modules)
  |- filestore/     (data_dir/filestore/<db>/ contents)

Defaults are encoach_v2 / yamenahmad / /tmp/odoo-data, all overridable
via env vars (DB, DB_USER, DATA_DIR, PGBIN, OUT_DIR). Output filename
is encoach_v2_YYYYMMDD_HHMMSS.zip in ./backups/.

Verified: latest run produced encoach_v2_20260425_031949.zip — 30 MB
compressed, 815 entries, 633 tables x 633 COPY data blocks, 575
filestore files, 94 modules in manifest.

Made-with: Cursor
2026-04-25 03:20:41 +04:00
Yamen Ahmad
e2aa8031ff feat(ai): LangGraph as core runtime + AI Agents/Tools console + full-demo seed
Core AI runtime
- New encoach.ai.agent + encoach.ai.tool models with M2M tool binding,
  graph topology (simple|plan_review_revise|rag|react), model + fallback,
  temperature, max_tokens, response_format, max_revisions, quality checks
  and system prompt fields.
- services/agent_runtime.py compiles a langgraph.StateGraph per agent
  and caches the build per (key, write_date). Emits a structured trace
  (output, tool_calls, retrieval_hits, revisions, quality_issues,
  ms, model_used, fallback_used) and auto-falls-back on rate-limit/5xx.
- services/agent_tools.py registers 11 tool handlers wrapping existing
  services: resources.search, rubric.fetch, outcomes.fetch,
  student.profile, quality.cefr_check, quality.ai_detect,
  quality.content_gate, course_plan.save (mutates),
  course_plan.save_materials (mutates), scoring.grade_writing,
  scoring.grade_speaking.
- 7 default agents seeded via data/agents_defaults.xml: course_planner,
  course_week_materials, exam_generator, exercise_generator, lms_tutor,
  writing_grader, speaking_grader.
- Feature flag encoach_ai.use_langgraph_runtime (default True).
- encoach_ai_course pipeline now routes through AgentRuntime when on,
  legacy SDK path kept as fallback.

Admin UI
- /admin/ai/prompts is now a tabbed Agents | Tools | Prompts console.
- AIAgentsPanel: card grid + config dialog (model/temp/graph/tools/
  system prompt) + built-in Test Runner showing live trace.
- AIToolsPanel: registry table with category badges, mutates flag,
  schema viewer, edit dialog.
- New /api/ai/agents* and /api/ai/tools* controller (list/get/update/
  test, list-tools, toggle-tool).
- Sidebar label nav.aiPrompts -> nav.aiAgents (AI Agents and Tools).
- EN + AR (RTL) translations for ~80 new keys.

Smart Wizard pages
- /admin/quick-setup hub + CourseWizard, CoursePlanWizard,
  RubricWizard, ExamStructureWizard step-by-step flows.
- /admin/course-plans list + detail pages.
- /teacher/quick-setup mirror.

Full demo seed + 8-role E2E
- seed_full_demo.py adds the 5 missing user_types (approver, corporate,
  mastercorporate, agent, developer), activates a 2-stage exam-approval
  workflow with one pending request, creates a GE1-aligned 12-week B1
  course plan with 6 detailed Week-1 materials (reading 400w, writing,
  listening 4-min script, speaking, grammar present simple vs continuous,
  vocabulary), and inserts sample ai.log + ai.feedback rows.
- reset_demo_passwords.py forces every demo login back to canonical
  passwords (admin123/teacher123/student123/approver123/corporate123/
  master123/agent123/dev123).
- e2e_full_scenario.py: 46/46 PASS read-only API smoke across all
  8 roles, including a live LangGraph round-trip on writing_grader.
- e2e_approval_chain.py: 6/6 PASS mutation E2E - approver approves
  stage 1, admin approves stage 2, linked encoach.exam.custom flips
  to status=published, verified via psql.

Docs
- docs/PROJECT_SUMMARY.md updated to 2026-04-25: new Latest events
  bullets, refreshed credentials table, full sections 22 (LangGraph
  runtime) and 23 (full demo seed + 8-role E2E).
- docs/ENCOACH_FULL_DEMO_QA_REPORT.md added with credentials,
  per-endpoint PASS/FAIL, mutation chain proof, LangGraph live output.
- backend/GE1 Course Outline_ Fall AY25-26.pdf vendored as the
  reference outline the GE1 plan/materials are aligned to.

Dependencies
- requirements.txt: langgraph>=0.2.0, langchain-core>=0.3.0.
- encoach_ai/__manifest__.py: external_dependencies updated.

Made-with: Cursor
2026-04-25 03:14:22 +04:00
Yamen Ahmad
1223074bde docs: add VPS restore runbook
Explains why /web/database/manager restores appear to succeed but the
live site keeps serving the old data (target DB name mismatch / Odoo
not restarted / dbfilter pinned), and ships a 6-step SSH-based
recovery procedure with safety-net backup, filestore handling, and
verification checklist tied to the known QA fixtures.

Made-with: Cursor
2026-04-21 09:35:11 +04:00
Yamen Ahmad
75ee0f1fe0 fix(ui): actionable 413/504/401 toasts + VPS nginx deploy template
Production QA reported "Submit failed 413" on /generation and plain
"Upload failed" (no detail) on resource upload. Root cause is the
outer reverse-proxy nginx on the VPS — still on default
`client_max_body_size 1m` — rejecting requests before they reach
Odoo. The inner docker-nginx and Odoo limits were already raised in
earlier commits; only the VPS proxy was left unpatched.

UI side
- Add `describeApiError(err, fallback, context)` helper in
  lib/api-client.ts. Maps common HTTP codes to human-readable,
  actionable toast descriptions (413 → "ask ops to raise nginx
  client_max_body_size", 504 → "server took too long, retry", 502
  → "Odoo is restarting", 401 → "sign in again", 4xx/5xx → server
  error body + status code).
- Use it in GenerationPage submit, ResourceManager upload,
  TeacherLibrary upload, CustomExamCreate save/publish. Replaces
  four slightly-different ad-hoc mappings with one source of truth.

Deploy side
- Add deploy/nginx-vps.conf.example: full TLS reverse-proxy template
  with the body/timeout knobs that must match Odoo's limit_request
  (128 MB) and limit_time_real (900 s).
- Add docs/DEPLOY_413_504_FIX.md: step-by-step remediation guide for
  the team lead covering the 3 layers that can block large bodies
  (outer nginx, Cloudflare, Odoo worker) and how to verify each.

Made-with: Cursor
2026-04-21 09:09:23 +04:00
Yamen Ahmad
d34180e107 fix(assignments): resolve op.student → res.users + student search
E2E QA surfaced a silent-drop bug in the exam-schedule endpoint: the
frontend's student picker sends `op.student.id` values (that's what
/api/students returns), but `encoach.exam.schedule.student_ids` is a
Many2many → res.users. The controller was writing the op.student id
straight through, so schedules linked to whichever res.users row
happened to share that integer id (OdooBot, other staff, etc.) — the
target student got 0 assignments and never saw the exam.

- exam_schedules.py: add `_resolve_student_user_ids` and use it in the
  create and update endpoints. Falls back to treating unmatched ids as
  direct res.users ids for back-office callers.
- AssignmentsPage.tsx: add a search input + larger list to the
  Individual Students picker; long lists were leaving Sarah unreachable
  inside the inner scroll container during QA.

Made-with: Cursor
2026-04-20 18:29:16 +04:00
Yamen Ahmad
eef3edf7e8 fix(i18n): default startup language to English
First-visit users now always land on the English UI regardless of
navigator.language. Arabic remains one click away via the toggle, and
the user's explicit pick is persisted to localStorage (encoach-lang)
and honoured on every subsequent load.

- src/i18n/index.ts: set lng: "en", drop navigator/htmlTag from the
  detector order so an Arabic-locale browser no longer silently boots
  the UI in Arabic before the user has chosen.
- src/lib/api-client.ts: mirror the same policy for the Accept-Language
  header sent to the backend, so AI-generated content stays English on
  first visit instead of echoing the browser locale.

Made-with: Cursor
2026-04-20 17:19:49 +04:00
Yamen Ahmad
a554ef5d42 fix(qa): approval queue, rubric UX, structure enforcement, upload/publish limits
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
2026-04-20 17:14:39 +04:00
b1b3d20eb4 Merge pull request 'fix(deploy): vendor OpenEduCat Community 19.0 under backend/' (#6) from v4 into main
Reviewed-on: devops/encoach_backend_new_v2#6
2026-04-20 08:08:32 +02:00
Yamen Ahmad
bab588b9da fix(deploy): vendor OpenEduCat Community 19.0 under backend/
Database dumps list 12 openeducat_* modules as installed (openeducat_core,
openeducat_admission, openeducat_assignment, openeducat_attendance,
openeducat_activity, openeducat_classroom, openeducat_exam, openeducat_facility,
openeducat_fees, openeducat_library, openeducat_parent, openeducat_timetable),
but the `backend/openeducat_erp-19.0/` folder was previously in .gitignore.
When a teammate restored the dump on the VPS Odoo failed with "module not
found" on every openeducat_* row in ir_module_module.

- Remove `backend/openeducat_erp-19.0/` from .gitignore and vendor the full
  LGPL-3 OpenEduCat Community 19.0 tree (14 modules: 12 core + theme + erp
  meta-module). `odoo.conf` and `odoo-docker.conf` already reference this
  path in `addons_path`, so restores now succeed on a fresh VPS with no
  extra bootstrap step.
- Strip the nested .git metadata that came with the upstream download so
  the tree is vendored flat (not as a submodule pointer).

Made-with: Cursor
2026-04-20 09:52:32 +04:00
e33a9a61bb Merge pull request 'v4' (#5) from v4 into main
Reviewed-on: devops/encoach_backend_new_v2#5
2026-04-19 16:16:33 +02:00
Yamen Ahmad
e1f059069f feat(i18n,rtl): full Arabic localization + RTL sweep across all layouts
Frontend
- i18n: install tailwindcss-rtl, Cairo font, RTL-aware direction in index.css.
- Language toggle: localize aria-label / menu label, persist choice, update
  document dir synchronously.
- Sidebar: add `side` prop so the drawer pins to the right in RTL; wire up
  AdminLmsLayout, RoleLayout (student/teacher) and AppSidebar to pass
  side = i18n.dir() === 'rtl' ? 'right' : 'left'.
- AdminLmsLayout: convert every nav item from hard-coded title to titleKey,
  translate group labels (incl. the collapsible Training), breadcrumbs,
  user menu (Profile / Settings / Logout), help button and toggle aria
  labels; replace physical mr-/right- utilities with logical me-/end-.
- AI components (AiTipBanner, AiInsightsPanel, AiAlertBanner, AiSearchBar,
  AiAssistantDrawer): apply dir="auto" at the container level, localize
  titles, loading / error / empty states.
- Dashboards (admin / student / teacher): wrap numeric values in <bdi>,
  localize dates via ar-EG, fix flex direction for KPI and assignment cards.
- UI primitives (breadcrumb, calendar, carousel, dropdown-menu, menubar,
  context-menu, pagination, sidebar): flip chevrons in RTL via a scoped
  CSS rule, swap pl-/pr-/ml-/mr- for ps-/pe-/ms-/me-.
- Add logical-direction helpers and bidirectional isolation classes.

Locales
- Expand en.ts and ar.ts with full `nav`, `sidebarGroup`, `breadcrumb`,
  `userMenu`, `chrome`, `ai`, and dashboard key sets; keep key parity.

API client
- `api-client.ts` reads the active language from localStorage/i18n and sends
  `Accept-Language` on every request so the backend can localize AI output.

Backend (encoach_ai)
- openai_service: add _LANGUAGE_NAMES, normalize_language, language-aware
  system prompt injection for every OpenAI call.
- coach_service + controllers (coach_controller, ai_controller): thread
  the requested language from headers / user locale down to OpenAIService.
- ai_feedback: fix latent registry error by pointing course_id at op.course
  instead of the non-existent encoach.course.

Other
- .gitignore: ignore runtime odoo logs and local caches.

Made-with: Cursor
2026-04-19 18:13:16 +04:00
7024197c7b Merge pull request 'v4' (#4) from v4 into main
Reviewed-on: devops/encoach_backend_new_v2#4
2026-04-19 12:33:30 +02:00