859 lines
26 KiB
JavaScript
859 lines
26 KiB
JavaScript
const { Lesson, Chapter, Subject, Vocab, Grammar, sequelize } = require('../models');
|
||
const { Op } = require('sequelize');
|
||
|
||
/**
|
||
* CREATE: Add new lesson
|
||
*/
|
||
exports.createLesson = async (req, res) => {
|
||
const transaction = await sequelize.transaction();
|
||
|
||
try {
|
||
const {
|
||
chapter_id,
|
||
lesson_number,
|
||
lesson_title,
|
||
lesson_type = 'json_content',
|
||
lesson_description,
|
||
content_json,
|
||
content_url,
|
||
content_type,
|
||
lesson_content_type,
|
||
duration_minutes,
|
||
is_published = false,
|
||
is_free = false,
|
||
display_order = 0,
|
||
thumbnail_url
|
||
} = req.body;
|
||
|
||
// Validate required fields
|
||
if (!chapter_id || !lesson_number || !lesson_title) {
|
||
await transaction.rollback();
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: 'Missing required fields: chapter_id, lesson_number, lesson_title'
|
||
});
|
||
}
|
||
|
||
// Validate content based on lesson_type
|
||
if (lesson_type === 'json_content' && !content_json) {
|
||
await transaction.rollback();
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: 'content_json is required when lesson_type is json_content'
|
||
});
|
||
}
|
||
|
||
if (lesson_type === 'url_content' && !content_url) {
|
||
await transaction.rollback();
|
||
return res.status(400).json({
|
||
success: false,
|
||
message: 'content_url is required when lesson_type is url_content'
|
||
});
|
||
}
|
||
|
||
// Check if chapter exists
|
||
const chapter = await Chapter.findByPk(chapter_id);
|
||
if (!chapter) {
|
||
await transaction.rollback();
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: 'Chapter not found'
|
||
});
|
||
}
|
||
|
||
// Create lesson
|
||
const lesson = await Lesson.create({
|
||
chapter_id,
|
||
lesson_number,
|
||
lesson_title,
|
||
lesson_type,
|
||
lesson_description,
|
||
content_json,
|
||
content_url,
|
||
content_type,
|
||
lesson_content_type,
|
||
duration_minutes,
|
||
is_published,
|
||
is_free,
|
||
display_order,
|
||
thumbnail_url
|
||
}, { transaction });
|
||
|
||
await transaction.commit();
|
||
|
||
res.status(201).json({
|
||
success: true,
|
||
message: 'Lesson created successfully',
|
||
data: lesson
|
||
});
|
||
|
||
} catch (error) {
|
||
await transaction.rollback();
|
||
console.error('Error creating lesson:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: 'Failed to create lesson',
|
||
error: error.message
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* READ: Get all lessons with pagination and filters
|
||
*/
|
||
exports.getAllLessons = async (req, res) => {
|
||
try {
|
||
const {
|
||
page = 1,
|
||
limit = 20,
|
||
chapter_id,
|
||
lesson_content_type,
|
||
lesson_type,
|
||
is_published,
|
||
is_free,
|
||
search
|
||
} = req.query;
|
||
|
||
const offset = (parseInt(page) - 1) * parseInt(limit);
|
||
const where = {};
|
||
|
||
if (chapter_id) where.chapter_id = chapter_id;
|
||
if (lesson_content_type) where.lesson_content_type = lesson_content_type;
|
||
if (lesson_type) where.lesson_type = lesson_type;
|
||
if (is_published !== undefined) where.is_published = is_published === 'true';
|
||
if (is_free !== undefined) where.is_free = is_free === 'true';
|
||
|
||
if (search) {
|
||
where[Op.or] = [
|
||
{ lesson_title: { [Op.like]: `%${search}%` } },
|
||
{ lesson_description: { [Op.like]: `%${search}%` } }
|
||
];
|
||
}
|
||
|
||
const { count, rows } = await Lesson.findAndCountAll({
|
||
where,
|
||
include: [
|
||
{
|
||
model: Chapter,
|
||
as: 'chapter',
|
||
attributes: ['id', 'chapter_title', 'chapter_number'],
|
||
include: [
|
||
{
|
||
model: Subject,
|
||
as: 'subject',
|
||
attributes: ['id', 'subject_name', 'subject_code']
|
||
}
|
||
]
|
||
}
|
||
],
|
||
limit: parseInt(limit),
|
||
offset,
|
||
order: [['display_order', 'ASC'], ['lesson_number', 'ASC']]
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
data: rows,
|
||
pagination: {
|
||
total: count,
|
||
page: parseInt(page),
|
||
limit: parseInt(limit),
|
||
totalPages: Math.ceil(count / parseInt(limit))
|
||
}
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Error fetching lessons:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: 'Failed to fetch lessons',
|
||
error: error.message
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* READ: Get lesson by ID
|
||
*/
|
||
exports.getLessonById = async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const lesson = await Lesson.findByPk(id, {
|
||
include: [
|
||
{
|
||
model: Chapter,
|
||
as: 'chapter',
|
||
include: [
|
||
{
|
||
model: Subject,
|
||
as: 'subject'
|
||
}
|
||
]
|
||
}
|
||
]
|
||
});
|
||
|
||
if (!lesson) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: 'Lesson not found'
|
||
});
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
data: lesson
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Error fetching lesson:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: 'Failed to fetch lesson',
|
||
error: error.message
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* UPDATE: Update lesson
|
||
*/
|
||
exports.updateLesson = async (req, res) => {
|
||
const transaction = await sequelize.transaction();
|
||
|
||
try {
|
||
const { id } = req.params;
|
||
const updateData = req.body;
|
||
|
||
const lesson = await Lesson.findByPk(id);
|
||
|
||
if (!lesson) {
|
||
await transaction.rollback();
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: 'Lesson not found'
|
||
});
|
||
}
|
||
|
||
await lesson.update(updateData, { transaction });
|
||
await transaction.commit();
|
||
|
||
res.json({
|
||
success: true,
|
||
message: 'Lesson updated successfully',
|
||
data: lesson
|
||
});
|
||
|
||
} catch (error) {
|
||
await transaction.rollback();
|
||
console.error('Error updating lesson:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: 'Failed to update lesson',
|
||
error: error.message
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* DELETE: Delete lesson
|
||
*/
|
||
exports.deleteLesson = async (req, res) => {
|
||
try {
|
||
const { id } = req.params;
|
||
|
||
const lesson = await Lesson.findByPk(id);
|
||
|
||
if (!lesson) {
|
||
return res.status(404).json({
|
||
success: false,
|
||
message: 'Lesson not found'
|
||
});
|
||
}
|
||
|
||
await lesson.destroy();
|
||
|
||
res.json({
|
||
success: true,
|
||
message: 'Lesson deleted successfully'
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Error deleting lesson:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: 'Failed to delete lesson',
|
||
error: error.message
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* READ: Get lessons by chapter
|
||
*/
|
||
exports.getLessonsByChapter = async (req, res) => {
|
||
try {
|
||
const { chapter_id } = req.params;
|
||
|
||
const lessons = await Lesson.findAll({
|
||
where: { chapter_id },
|
||
order: [['display_order', 'ASC'], ['lesson_number', 'ASC']]
|
||
});
|
||
|
||
res.json({
|
||
success: true,
|
||
data: lessons,
|
||
count: lessons.length
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Error fetching lessons by chapter:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: 'Failed to fetch lessons by chapter',
|
||
error: error.message
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* GET GUIDE: Comprehensive guide for AI to create learning content
|
||
*/
|
||
exports.getLearningContentGuide = async (req, res) => {
|
||
try {
|
||
const guide = {
|
||
guide_version: "1.0.0",
|
||
last_updated: new Date().toISOString(),
|
||
|
||
hierarchy: {
|
||
description: "Learning content hierarchy: Subject → Chapter → Lesson",
|
||
structure: {
|
||
subject: "Top-level course/curriculum (e.g., 'English Grade 1')",
|
||
chapter: "Main topic within subject (e.g., 'Unit 1: My Family')",
|
||
lesson: "Individual learning activity (e.g., 'Lesson 1: Family Vocabulary')"
|
||
}
|
||
},
|
||
|
||
subject_structure: {
|
||
description: "Subject represents a complete course or curriculum",
|
||
required_fields: {
|
||
subject_code: {
|
||
type: "string(20)",
|
||
example: "ENG-G1",
|
||
description: "Unique code for subject"
|
||
},
|
||
subject_name: {
|
||
type: "string(100)",
|
||
example: "English Grade 1",
|
||
description: "Subject name"
|
||
}
|
||
},
|
||
optional_fields: {
|
||
subject_name_en: "English name",
|
||
description: "Subject description",
|
||
is_active: "Active status (default: true)",
|
||
is_premium: "Premium content flag",
|
||
is_training: "Training content flag",
|
||
is_public: "Public self-learning flag"
|
||
}
|
||
},
|
||
|
||
chapter_structure: {
|
||
description: "Chapter represents a major topic within a subject",
|
||
required_fields: {
|
||
subject_id: {
|
||
type: "UUID",
|
||
example: "550e8400-e29b-41d4-a716-446655440000",
|
||
description: "Parent subject ID"
|
||
},
|
||
chapter_number: {
|
||
type: "integer",
|
||
example: 1,
|
||
description: "Sequential chapter number"
|
||
},
|
||
chapter_title: {
|
||
type: "string(200)",
|
||
example: "Unit 1: My Family",
|
||
description: "Chapter title"
|
||
}
|
||
},
|
||
optional_fields: {
|
||
chapter_description: "Chapter description",
|
||
duration_minutes: "Estimated duration",
|
||
is_published: "Published status (default: false)",
|
||
display_order: "Custom display order"
|
||
}
|
||
},
|
||
|
||
lesson_structure: {
|
||
description: "Lesson represents individual learning activity",
|
||
required_fields: {
|
||
chapter_id: {
|
||
type: "UUID",
|
||
example: "550e8400-e29b-41d4-a716-446655440000",
|
||
description: "Parent chapter ID"
|
||
},
|
||
lesson_number: {
|
||
type: "integer",
|
||
example: 1,
|
||
description: "Sequential lesson number"
|
||
},
|
||
lesson_title: {
|
||
type: "string(200)",
|
||
example: "Family Vocabulary",
|
||
description: "Lesson title"
|
||
},
|
||
lesson_type: {
|
||
type: "enum",
|
||
options: ["json_content", "url_content"],
|
||
default: "json_content",
|
||
description: "Content type: JSON or URL"
|
||
}
|
||
},
|
||
optional_fields: {
|
||
lesson_description: "Lesson description",
|
||
lesson_content_type: "Content category: vocabulary, grammar, phonics, review, mixed",
|
||
content_json: "JSON content structure (see lesson_content_types below)",
|
||
content_url: "URL for external content",
|
||
content_type: "URL content type: video, audio, pdf, etc.",
|
||
duration_minutes: "Estimated duration",
|
||
is_published: "Published status",
|
||
is_free: "Free trial access",
|
||
display_order: "Custom display order",
|
||
thumbnail_url: "Lesson thumbnail"
|
||
}
|
||
},
|
||
|
||
lesson_content_types: {
|
||
vocabulary_lesson: {
|
||
description: "Vocabulary learning lesson with word list and exercises. System will lookup words in Vocab table when needed.",
|
||
structure: {
|
||
type: "vocabulary",
|
||
words: {
|
||
type: "array of strings OR array of objects",
|
||
description: "List of vocabulary words. System will search in Vocab table by base_word. Can be simple array of words or detailed objects.",
|
||
example_simple: ["mother", "father", "sister", "brother"],
|
||
example_detailed: [
|
||
{
|
||
word: "mother",
|
||
translation: "mẹ",
|
||
image: "https://cdn.sena.tech/vocab/mother.jpg",
|
||
audio: "https://cdn.sena.tech/audio/mother.mp3",
|
||
phonetic: "/ˈmʌð.ər/"
|
||
}
|
||
],
|
||
note: "Use simple array for words already in Vocab table. Use detailed objects for new/custom vocabulary."
|
||
},
|
||
exercises: {
|
||
type: "array of objects",
|
||
description: "Practice exercises",
|
||
example: [
|
||
{
|
||
type: "match",
|
||
question: "Match the words with pictures",
|
||
items: [
|
||
{ word: "mother", image: "..." },
|
||
{ word: "father", image: "..." }
|
||
]
|
||
},
|
||
{
|
||
type: "fill_blank",
|
||
question: "This is my ___",
|
||
answer: "mother",
|
||
options: ["mother", "father", "sister"]
|
||
}
|
||
]
|
||
}
|
||
},
|
||
example: {
|
||
type: "vocabulary",
|
||
words: ["mother", "father", "sister", "brother"],
|
||
exercises: [
|
||
{
|
||
type: "match",
|
||
question: "Match family members",
|
||
items: [
|
||
{ word: "mother", image: "https://cdn.sena.tech/img/mother.jpg" },
|
||
{ word: "father", image: "https://cdn.sena.tech/img/father.jpg" }
|
||
]
|
||
}
|
||
],
|
||
note: "System will lookup these words in Vocab table when rendering lesson"
|
||
}
|
||
},
|
||
|
||
grammar_lesson: {
|
||
description: "Grammar lesson with sentences and examples. System will lookup grammar patterns when needed.",
|
||
structure: {
|
||
type: "grammar",
|
||
grammar_points: {
|
||
type: "array of strings OR array of objects",
|
||
description: "Grammar points to teach. Can be grammar names or sentence patterns. System will search in Grammar table.",
|
||
example_simple: ["Present Simple", "Present Continuous"],
|
||
example_sentences: ["I eat an apple", "She eats an apple", "They are eating"],
|
||
note: "Use grammar names (e.g., 'Present Simple') or example sentences. System will find matching patterns in Grammar table."
|
||
},
|
||
sentences: {
|
||
type: "array of strings OR array of objects",
|
||
description: "Example sentences for this lesson",
|
||
example: ["I eat an apple", "She drinks water", "He plays football"]
|
||
},
|
||
examples: {
|
||
type: "array of objects",
|
||
description: "Example sentences",
|
||
example: [
|
||
{
|
||
sentence: "I eat an apple.",
|
||
translation: "Tôi ăn một quả táo.",
|
||
audio: "https://cdn.sena.tech/audio/example1.mp3"
|
||
}
|
||
]
|
||
},
|
||
exercises: {
|
||
type: "array of objects",
|
||
description: "Practice exercises",
|
||
example: [
|
||
{
|
||
type: "fill_blank",
|
||
question: "She ___ (eat) an apple every day.",
|
||
answer: "eats",
|
||
options: ["eat", "eats", "eating"]
|
||
},
|
||
{
|
||
type: "arrange_words",
|
||
question: "Arrange: apple / eat / I / an",
|
||
answer: "I eat an apple"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
example: {
|
||
type: "grammar",
|
||
grammar_points: ["Present Simple"],
|
||
sentences: [
|
||
"I eat an apple.",
|
||
"She eats an apple.",
|
||
"They eat apples."
|
||
],
|
||
exercises: [
|
||
{
|
||
type: "fill_blank",
|
||
question: "She ___ an apple.",
|
||
answer: "eats",
|
||
options: ["eat", "eats", "eating"]
|
||
}
|
||
]
|
||
}
|
||
},
|
||
|
||
phonics_lesson: {
|
||
description: "Phonics/pronunciation lesson with IPA and sound practice",
|
||
structure: {
|
||
type: "phonics",
|
||
phonics_rules: {
|
||
type: "array of objects",
|
||
description: "Phonics rules with IPA and example words",
|
||
example: [
|
||
{
|
||
ipa: "/æ/",
|
||
sound_name: "short a",
|
||
description: "As in 'cat', 'hat'",
|
||
audio: "https://cdn.sena.tech/phonics/ae.mp3",
|
||
words: [
|
||
{ word: "cat", phonetic: "/kæt/", audio: "..." },
|
||
{ word: "hat", phonetic: "/hæt/", audio: "..." }
|
||
]
|
||
}
|
||
]
|
||
},
|
||
exercises: {
|
||
type: "array of objects",
|
||
description: "Pronunciation practice",
|
||
example: [
|
||
{
|
||
type: "listen_repeat",
|
||
question: "Listen and repeat",
|
||
audio: "https://cdn.sena.tech/audio/cat.mp3",
|
||
word: "cat"
|
||
},
|
||
{
|
||
type: "identify_sound",
|
||
question: "Which word has the /æ/ sound?",
|
||
options: ["cat", "cut", "cot"],
|
||
answer: "cat"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
example: {
|
||
type: "phonics",
|
||
phonics_rules: [
|
||
{
|
||
ipa: "/æ/",
|
||
sound_name: "short a",
|
||
audio: "https://cdn.sena.tech/phonics/ae.mp3",
|
||
words: [
|
||
{ word: "cat", phonetic: "/kæt/" },
|
||
{ word: "hat", phonetic: "/hæt/" }
|
||
]
|
||
}
|
||
],
|
||
exercises: [
|
||
{
|
||
type: "listen_repeat",
|
||
audio: "https://cdn.sena.tech/audio/cat.mp3",
|
||
word: "cat"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
|
||
review_lesson: {
|
||
description: "Review lesson combining vocabulary, grammar, and phonics",
|
||
structure: {
|
||
type: "review",
|
||
sections: {
|
||
type: "array of objects",
|
||
description: "Array containing vocabulary, grammar, and/or phonics sections",
|
||
example: [
|
||
{
|
||
section_type: "vocabulary",
|
||
title: "Family Vocabulary Review",
|
||
vocabulary_ids: [],
|
||
exercises: []
|
||
},
|
||
{
|
||
section_type: "grammar",
|
||
title: "Present Simple Review",
|
||
grammar_ids: [],
|
||
exercises: []
|
||
},
|
||
{
|
||
section_type: "phonics",
|
||
title: "Short A Sound Review",
|
||
phonics_rules: [],
|
||
exercises: []
|
||
}
|
||
]
|
||
},
|
||
overall_exercises: {
|
||
type: "array of objects",
|
||
description: "Mixed exercises covering all sections",
|
||
example: [
|
||
{
|
||
type: "mixed_quiz",
|
||
questions: [
|
||
{ type: "vocabulary", question: "...", answer: "..." },
|
||
{ type: "grammar", question: "...", answer: "..." }
|
||
]
|
||
}
|
||
]
|
||
}
|
||
},
|
||
example: {
|
||
type: "review",
|
||
sections: [
|
||
{
|
||
section_type: "vocabulary",
|
||
title: "Family Words",
|
||
words: ["mother", "father", "sister", "brother"]
|
||
},
|
||
{
|
||
section_type: "grammar",
|
||
title: "Present Simple",
|
||
grammar_points: ["Present Simple"],
|
||
sentences: ["I eat an apple", "She eats an apple"]
|
||
},
|
||
{
|
||
section_type: "phonics",
|
||
title: "Short A Sound",
|
||
phonics_rules: [
|
||
{
|
||
ipa: "/æ/",
|
||
words: ["cat", "hat", "bat"]
|
||
}
|
||
]
|
||
}
|
||
],
|
||
overall_exercises: [
|
||
{
|
||
type: "mixed_quiz",
|
||
questions: [
|
||
{ type: "vocabulary", question: "What is 'mẹ' in English?", answer: "mother" },
|
||
{ type: "grammar", question: "She ___ an apple", answer: "eats" }
|
||
]
|
||
}
|
||
],
|
||
note: "System will lookup words and grammar patterns from database when rendering"
|
||
}
|
||
}
|
||
},
|
||
|
||
exercise_types: {
|
||
description: "Common exercise types across all lesson types",
|
||
types: {
|
||
match: "Match items (words to images, words to translations)",
|
||
fill_blank: "Fill in the blank",
|
||
multiple_choice: "Multiple choice question",
|
||
arrange_words: "Arrange words to form sentence",
|
||
listen_repeat: "Listen and repeat (for phonics)",
|
||
identify_sound: "Identify the correct sound/word",
|
||
true_false: "True or false question",
|
||
mixed_quiz: "Mixed questions from different types"
|
||
}
|
||
},
|
||
|
||
api_workflow: {
|
||
description: "Step-by-step workflow to create learning content. No need to create Vocab/Grammar entries first - just use word lists and sentences directly.",
|
||
steps: [
|
||
{
|
||
step: 1,
|
||
action: "Create Subject",
|
||
endpoint: "POST /api/subjects",
|
||
example: {
|
||
subject_code: "ENG-G1",
|
||
subject_name: "English Grade 1"
|
||
}
|
||
},
|
||
{
|
||
step: 2,
|
||
action: "Create Chapter",
|
||
endpoint: "POST /api/chapters",
|
||
example: {
|
||
subject_id: "<subject_uuid>",
|
||
chapter_number: 1,
|
||
chapter_title: "Unit 1: My Family"
|
||
}
|
||
},
|
||
{
|
||
step: 3,
|
||
action: "Create Vocabulary Lesson (using word list)",
|
||
endpoint: "POST /api/learning-content/lessons",
|
||
example: {
|
||
chapter_id: "<chapter_uuid>",
|
||
lesson_number: 1,
|
||
lesson_title: "Family Vocabulary",
|
||
lesson_type: "json_content",
|
||
lesson_content_type: "vocabulary",
|
||
content_json: {
|
||
type: "vocabulary",
|
||
words: ["mother", "father", "sister", "brother"],
|
||
exercises: []
|
||
}
|
||
},
|
||
note: "System will search for these words in Vocab table when rendering lesson"
|
||
},
|
||
{
|
||
step: 4,
|
||
action: "Create Grammar Lesson (using sentences)",
|
||
endpoint: "POST /api/learning-content/lessons",
|
||
example: {
|
||
chapter_id: "<chapter_uuid>",
|
||
lesson_number: 2,
|
||
lesson_title: "Present Simple",
|
||
lesson_type: "json_content",
|
||
lesson_content_type: "grammar",
|
||
content_json: {
|
||
type: "grammar",
|
||
grammar_points: ["Present Simple"],
|
||
sentences: ["I eat an apple", "She eats an apple"],
|
||
exercises: []
|
||
}
|
||
},
|
||
note: "System will find matching grammar patterns from Grammar table"
|
||
},
|
||
{
|
||
step: 5,
|
||
action: "Create Review Lesson",
|
||
endpoint: "POST /api/learning-content/lessons",
|
||
example: {
|
||
chapter_id: "<chapter_uuid>",
|
||
lesson_number: 3,
|
||
lesson_title: "Unit Review",
|
||
lesson_type: "json_content",
|
||
lesson_content_type: "review",
|
||
content_json: {
|
||
type: "review",
|
||
sections: [
|
||
{ section_type: "vocabulary", words: ["mother", "father"] },
|
||
{ section_type: "grammar", sentences: ["I eat an apple"] }
|
||
],
|
||
overall_exercises: []
|
||
}
|
||
}
|
||
}
|
||
]
|
||
},
|
||
|
||
validation_checklist: [
|
||
"✓ Subject, Chapter, Lesson hierarchy is maintained",
|
||
"✓ lesson_type matches content (json_content has content_json, url_content has content_url)",
|
||
"✓ lesson_content_type is set correctly (vocabulary, grammar, phonics, review)",
|
||
"✓ content_json structure matches lesson_content_type",
|
||
"✓ For vocabulary lessons: words array is provided (system will lookup in Vocab table)",
|
||
"✓ For grammar lessons: sentences or grammar_points are provided (system will lookup in Grammar table)",
|
||
"✓ Exercise types are valid",
|
||
"✓ URLs are accessible (audio, images, videos)",
|
||
"✓ Lesson numbers are sequential within chapter"
|
||
],
|
||
|
||
common_mistakes: [
|
||
{
|
||
mistake: "Missing parent references",
|
||
example: { lesson_title: "Vocab" },
|
||
fix: { chapter_id: "<uuid>", lesson_title: "Vocab" },
|
||
explanation: "Always provide chapter_id for lessons"
|
||
},
|
||
{
|
||
mistake: "Wrong content type",
|
||
example: { lesson_type: "json_content", content_url: "..." },
|
||
fix: { lesson_type: "url_content", content_url: "..." },
|
||
explanation: "lesson_type must match content field"
|
||
},
|
||
{
|
||
mistake: "Using old vocabulary_ids format",
|
||
example: { vocabulary_ids: ["uuid1", "uuid2"] },
|
||
fix: { words: ["mother", "father", "sister"] },
|
||
explanation: "Use words array instead of vocabulary_ids. System will lookup words in Vocab table."
|
||
},
|
||
{
|
||
mistake: "Using old grammar_ids format",
|
||
example: { grammar_ids: ["uuid1"] },
|
||
fix: { grammar_points: ["Present Simple"], sentences: ["I eat an apple"] },
|
||
explanation: "Use grammar_points or sentences instead of grammar_ids. System will find patterns in Grammar table."
|
||
},
|
||
{
|
||
mistake: "Missing content_json type",
|
||
example: { content_json: { exercises:[]} },
|
||
fix: { content_json: { type: "vocabulary", exercises: [] } },
|
||
explanation: "content_json must have type field"
|
||
}
|
||
],
|
||
|
||
ai_tips: {
|
||
planning: "Plan subject → chapter → lesson hierarchy before creating",
|
||
word_lists: "Use simple word arrays like ['mother', 'father']. System will automatically lookup in Vocab table when rendering.",
|
||
sentences: "Use sentence arrays like ['I eat an apple', 'She eats an apple']. System will find matching grammar patterns.",
|
||
consistency: "Keep lesson_content_type and content_json.type consistent",
|
||
exercises: "Include diverse exercise types for better engagement",
|
||
multimedia: "Provide audio and images for better learning experience",
|
||
no_ids_needed: "No need to create Vocab/Grammar entries first - just use word lists and sentences directly!"
|
||
}
|
||
};
|
||
|
||
res.json({
|
||
success: true,
|
||
data: guide
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error('Error generating learning content guide:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
message: 'Failed to generate learning content guide',
|
||
error: error.message
|
||
});
|
||
}
|
||
};
|