# Course Cloning API ## 📋 Overview API endpoint for cloning an entire course including all content, lessons, quizzes, and attachments. --- ## 🔧 Clone Course ### Endpoint ```http POST /api/instructor/courses/:courseId/clone Authorization: Bearer Content-Type: application/json ``` ### Request Body ```json { "new_title": { "th": "Python สำหรับผู้เริ่มต้น (ปรับปรุง 2026)", "en": "Python for Beginners (Updated 2026)" }, "new_slug": "python-beginners-2026", "copy_settings": { "copy_chapters": true, "copy_lessons": true, "copy_quizzes": true, "copy_attachments": true, "copy_announcements": false } } ``` ### Response 201 ```json { "new_course_id": 25, "cloned_from_course_id": 1, "status": "DRAFT", "message": "Course cloned successfully", "cloned_content": { "chapters": 10, "lessons": 45, "quizzes": 10, "questions": 150, "attachments": 30, "total_duration_minutes": 1200 }, "next_steps": [ "Edit content as needed", "Update videos if necessary", "Submit for approval when ready" ] } ``` --- ## 📝 What Gets Cloned ### ✅ Copied: - Course info (title, description, price, thumbnail) - All chapters - All lessons (including video references) - All quizzes and questions - All attachments (files copied to new location) - Course settings (is_free, have_certificate) - Lesson prerequisites - Sort orders ### ❌ NOT Copied: - Enrollments - Student progress - Reviews/ratings - Announcements (optional) - Approval status (new course = DRAFT) - Statistics --- ## 🎯 Use Cases ### 1. Update Videos ``` 1. Clone course 2. Replace videos in new course 3. Submit for approval 4. Publish ``` ### 2. Add Lessons ``` 1. Clone course 2. Add new lessons/chapters 3. Submit for approval 4. Publish ``` ### 3. Major Restructure ``` 1. Clone course 2. Reorganize chapters/lessons 3. Update content 4. Submit for approval 5. Publish ``` --- ## 🔒 Permissions **Who can clone**: - ✅ Course owner (created_by) - ✅ Primary instructor (is_primary = true) - ✅ Admin **Restrictions**: - ❌ Cannot clone archived courses - ❌ Cannot clone rejected courses - ✅ Can clone approved/published courses --- ## 📊 Clone Process ### Backend Process: ```javascript async function cloneCourse(originalCourseId, newData) { // 1. Create new course const newCourse = await createCourse({ ...originalCourse, title: newData.new_title, slug: newData.new_slug, status: 'DRAFT', created_by: currentUser.id }); // 2. Clone chapters for (const chapter of originalChapters) { const newChapter = await createChapter({ ...chapter, course_id: newCourse.id }); // 3. Clone lessons for (const lesson of chapter.lessons) { const newLesson = await createLesson({ ...lesson, chapter_id: newChapter.id }); // 4. Clone attachments (copy files) for (const attachment of lesson.attachments) { await copyFileToS3(attachment.file_path, newPath); await createAttachment({ ...attachment, lesson_id: newLesson.id, file_path: newPath }); } // 5. Clone quizzes if (lesson.quiz) { const newQuiz = await createQuiz({ ...lesson.quiz, lesson_id: newLesson.id }); // 6. Clone questions for (const question of lesson.quiz.questions) { const newQuestion = await createQuestion({ ...question, quiz_id: newQuiz.id }); // 7. Clone choices for (const choice of question.choices) { await createChoice({ ...choice, question_id: newQuestion.id }); } } } } } return newCourse; } ``` --- ## ⚠️ Error Handling ### Error: Course Not Found ```json { "error": { "code": "COURSE_NOT_FOUND", "message": "Course not found or you don't have permission" } } ``` ### Error: Slug Already Exists ```json { "error": { "code": "SLUG_EXISTS", "message": "Course slug already exists. Please use a different slug.", "suggestion": "python-beginners-2026-v2" } } ``` ### Error: Clone Failed ```json { "error": { "code": "CLONE_FAILED", "message": "Failed to clone course. Please try again.", "details": "Error copying attachment: file.pdf" } } ``` --- ## 🎨 Frontend Integration ### Clone Button ```javascript async function cloneCourse(courseId) { const response = await fetch(`/api/instructor/courses/${courseId}/clone`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ new_title: { th: `${originalTitle.th} (คัดลอก)`, en: `${originalTitle.en} (Copy)` }, new_slug: `${originalSlug}-copy-${Date.now()}` }) }); const data = await response.json(); if (response.ok) { // Redirect to edit new course window.location.href = `/instructor/courses/${data.new_course_id}/edit`; } } ``` --- ## 📈 Performance Considerations ### Large Courses: - Use background job for cloning - Show progress indicator - Send notification when complete ```http POST /api/instructor/courses/:courseId/clone { "async": true } Response 202: { "job_id": "clone-job-123", "status": "PROCESSING", "message": "Clone in progress. You will be notified when complete.", "estimated_time_minutes": 5 } ``` ### Check Job Status: ```http GET /api/instructor/clone-jobs/:jobId Response: { "job_id": "clone-job-123", "status": "COMPLETED", "new_course_id": 25, "progress": { "chapters": "10/10", "lessons": "45/45", "quizzes": "10/10" } } ``` --- ## 🔗 Related Endpoints ### Get Clone History ```http GET /api/instructor/courses/:courseId/clones ``` **Response**: ```json { "original_course_id": 1, "clones": [ { "id": 25, "title": "Python for Beginners (Updated 2026)", "status": "PUBLISHED", "cloned_at": "2026-01-06T15:00:00Z", "cloned_by": "Prof. John Smith" }, { "id": 30, "title": "Python for Beginners (Advanced)", "status": "DRAFT", "cloned_at": "2026-01-05T10:00:00Z" } ] } ``` --- ## ✅ Summary **Endpoint**: `POST /instructor/courses/:courseId/clone` **Copies**: - ✅ All chapters, lessons, quizzes - ✅ All attachments (files copied) - ✅ All settings and configurations **Does NOT copy**: - ❌ Enrollments or student data - ❌ Reviews or ratings - ❌ Approval status **Result**: New DRAFT course ready for editing --- ## 🚀 Next Steps After cloning: 1. Edit new course as needed 2. Update videos if necessary 3. Add/remove lessons 4. Submit for approval 5. Publish when approved