feat(course-plan): RAG sources + multi-modal media + assignments + student view
Some checks failed
Deploy Frontend to Staging / Deploy frontend to staging (push) Has been cancelled
Some checks failed
Deploy Frontend to Staging / Deploy frontend to staging (push) Has been cancelled
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
This commit is contained in:
@@ -105,6 +105,7 @@ const en: Translations = {
|
||||
dashboard: "Dashboard",
|
||||
courses: "Courses",
|
||||
myCourses: "My Courses",
|
||||
myCoursePlans: "My Course Plans",
|
||||
students: "Students",
|
||||
teachers: "Teachers",
|
||||
batches: "Batches",
|
||||
@@ -796,8 +797,14 @@ const en: Translations = {
|
||||
basicsDesc: "Name the course and set level, duration, and weekly hours.",
|
||||
coverage: "Coverage",
|
||||
coverageDesc: "Tell the AI how hours split across skills and who the learners are.",
|
||||
sources: "Reference sources",
|
||||
sourcesDesc:
|
||||
"Drop PDFs, URLs, or pasted text. The AI uses them as grounding when generating each week's materials.",
|
||||
scope: "Scope",
|
||||
scopeDesc: "Optional: grammar focus, resources to reference, free-form notes.",
|
||||
media: "Multimedia",
|
||||
mediaDesc:
|
||||
"Pick which media the AI should auto-produce alongside the text materials.",
|
||||
review: "Review",
|
||||
reviewDesc: "Double-check your brief before kicking off the generation.",
|
||||
},
|
||||
@@ -825,7 +832,156 @@ const en: Translations = {
|
||||
cefrRequired: "Please pick a CEFR level.",
|
||||
weeksRange: "Total weeks must be at least 1.",
|
||||
},
|
||||
review: {
|
||||
sourcesCount_one: "{{count}} reference source",
|
||||
sourcesCount_other: "{{count}} reference sources",
|
||||
noSources: "No reference sources",
|
||||
mediaNone: "No media generation",
|
||||
},
|
||||
},
|
||||
sections_extras: {
|
||||
sources: "Reference sources",
|
||||
progress: "Generation progress",
|
||||
assignments: "Assignments",
|
||||
media: "Generated media",
|
||||
},
|
||||
sources: {
|
||||
sectionTitle: "Reference sources (RAG)",
|
||||
sectionDesc:
|
||||
"Upload PDFs, paste URLs, or drop in raw text. The AI will use the indexed content as grounding when it writes weekly materials.",
|
||||
collected_one: "{{count}} source ready",
|
||||
collected_other: "{{count}} sources ready",
|
||||
empty: "No reference sources yet.",
|
||||
dropTitle: "Add reference materials",
|
||||
dropHint: "PDF, DOCX, TXT, HTML, or plain text — up to a few MB each.",
|
||||
uploadFiles: "Upload files",
|
||||
urlLabel: "Add a URL",
|
||||
textLabel: "Or paste text directly",
|
||||
textPlaceholder: "Paste a passage to ground the AI on…",
|
||||
uploadFailed: "Couldn't upload \"{{name}}\".",
|
||||
reindex: "Reindex",
|
||||
reindexed: "Source reindexed.",
|
||||
reindexFailed: "Reindex failed.",
|
||||
deleted: "Source deleted.",
|
||||
deleteFailed: "Couldn't delete source.",
|
||||
status: {
|
||||
pending: "Pending",
|
||||
indexing: "Indexing…",
|
||||
indexed: "Indexed",
|
||||
failed: "Failed",
|
||||
},
|
||||
kindLabel: {
|
||||
file: "File",
|
||||
url: "URL",
|
||||
text: "Text",
|
||||
},
|
||||
chunks_one: "{{count}} chunk",
|
||||
chunks_other: "{{count}} chunks",
|
||||
},
|
||||
sourceKind: {
|
||||
file: "File",
|
||||
url: "URL",
|
||||
text: "Inline text",
|
||||
},
|
||||
deliverables: {
|
||||
title: "Generation progress",
|
||||
subtitle:
|
||||
"What the AI is going to produce, what it has produced, and what is fully ready (materials + media).",
|
||||
summary: {
|
||||
planned_one: "{{count}} planned",
|
||||
planned_other: "{{count}} planned",
|
||||
generated_one: "{{count}} generated",
|
||||
generated_other: "{{count}} generated",
|
||||
ready_one: "{{count}} ready",
|
||||
ready_other: "{{count}} ready",
|
||||
},
|
||||
mediaCounts: "Audio {{audio}} · Image {{image}} · Video {{video}}",
|
||||
status: {
|
||||
planned: "Planned",
|
||||
generated: "Generated",
|
||||
ready: "Ready",
|
||||
},
|
||||
perWeek: "Per-week breakdown",
|
||||
noWeeks: "No weeks yet — generate the plan first.",
|
||||
},
|
||||
media: {
|
||||
audio: "Audio",
|
||||
image: "Image",
|
||||
video: "Video",
|
||||
audioTitle: "Narration audio",
|
||||
audioDesc:
|
||||
"Generate spoken audio (Polly / ElevenLabs) for listening scripts and speaking prompts.",
|
||||
imageTitle: "Cover images",
|
||||
imageDesc:
|
||||
"Generate DALL·E 3 images for reading texts and listening scripts. Counts against the per-plan image budget.",
|
||||
videoTitle: "Slideshow video",
|
||||
videoDesc:
|
||||
"Combine audio + image into a short MP4 with ffmpeg. Slowest option.",
|
||||
hint:
|
||||
"Audio is on by default. Video is off by default — turn it on once you've reviewed the audio + image.",
|
||||
drawerTitle: "Media for \"{{title}}\"",
|
||||
drawerSubtitle: "Generate or remove audio, images, and slideshow video.",
|
||||
generateAudio: "Generate audio",
|
||||
generateImage: "Generate image",
|
||||
generateVideo: "Generate video",
|
||||
generating: "Generating…",
|
||||
audioReady: "Audio ready",
|
||||
imageReady: "Image ready",
|
||||
videoReady: "Video ready",
|
||||
audioFailed: "Audio generation failed.",
|
||||
imageFailed: "Image generation failed.",
|
||||
videoFailed: "Video generation failed.",
|
||||
deleted: "Media deleted.",
|
||||
deleteFailed: "Couldn't delete media.",
|
||||
open: "Open",
|
||||
download: "Download",
|
||||
noMedia: "No media yet for this material.",
|
||||
mediaForMaterial: "Media",
|
||||
bulk: "Generate week media",
|
||||
bulkSuccess: "Week media generation done.",
|
||||
bulkFailed: "Couldn't generate week media.",
|
||||
provider: "Provider",
|
||||
},
|
||||
assignments: {
|
||||
title: "Assigned to",
|
||||
empty: "Not assigned to anyone yet.",
|
||||
assign: "Assign",
|
||||
assignTitle: "Assign this course plan",
|
||||
assignSubtitle:
|
||||
"Pick a class (batch) for everyone, or individual students. They'll see the plan in their student dashboard.",
|
||||
modeBatch: "Whole class (batch)",
|
||||
modeStudents: "Individual students",
|
||||
pickBatch: "Select a batch",
|
||||
pickStudents: "Pick students",
|
||||
noBatches: "No batches available.",
|
||||
noStudents: "No students available.",
|
||||
dueDate: "Due date (optional)",
|
||||
message: "Message to learners (optional)",
|
||||
messagePlaceholder: "Welcome note, expectations, deadlines…",
|
||||
created: "Plan assigned.",
|
||||
createFailed: "Couldn't assign plan.",
|
||||
removed: "Assignment removed.",
|
||||
removeFailed: "Couldn't remove assignment.",
|
||||
remove: "Remove",
|
||||
confirmRemove: "Remove this assignment?",
|
||||
assignedBy: "Assigned by {{name}}",
|
||||
students_one: "{{count}} student",
|
||||
students_other: "{{count}} students",
|
||||
cancel: "Cancel",
|
||||
save: "Assign",
|
||||
},
|
||||
student: {
|
||||
listTitle: "My course plans",
|
||||
listSubtitle:
|
||||
"Personalised AI-generated curricula assigned by your teacher. Open one to see the weekly plan, materials, and media.",
|
||||
empty: "No plans assigned to you yet.",
|
||||
open: "Open course",
|
||||
assignedBy: "Assigned by {{name}}",
|
||||
due: "Due {{date}}",
|
||||
noDue: "No deadline",
|
||||
backToList: "Back to my plans",
|
||||
},
|
||||
wizardSourcesStep: "Sources",
|
||||
},
|
||||
aiAdmin: {
|
||||
title: "AI Agents & Tools",
|
||||
|
||||
Reference in New Issue
Block a user