| | import { Document, Packer, Paragraph, TextRun, HeadingLevel, AlignmentType } from 'docx'; |
| | import { GeneratedQuestion } from '@/types/quiz'; |
| |
|
| | export async function exportQuestionsToDocx( |
| | questions: GeneratedQuestion[], |
| | filename: string = 'quiz-questions.docx', |
| | includeAnswers: boolean = true |
| | ) { |
| | |
| | const doc = new Document({ |
| | sections: [{ |
| | properties: {}, |
| | children: [ |
| | |
| | new Paragraph({ |
| | text: 'Quiz Questions', |
| | heading: HeadingLevel.HEADING_1, |
| | alignment: AlignmentType.CENTER, |
| | spacing: { |
| | after: 400, |
| | } |
| | }), |
| | |
| | |
| | new Paragraph({ |
| | text: `Generated: ${new Date().toLocaleDateString()}`, |
| | alignment: AlignmentType.CENTER, |
| | spacing: { |
| | after: 200 |
| | } |
| | }), |
| | |
| | |
| | new Paragraph({ |
| | text: `Total Questions: ${questions.length}`, |
| | alignment: AlignmentType.CENTER, |
| | spacing: { |
| | after: 300 |
| | } |
| | }), |
| | |
| | |
| | ...questions.map((question, index) => |
| | renderQuestion(question, index + 1, includeAnswers) |
| | ).flat() |
| | ] |
| | }] |
| | }); |
| |
|
| | |
| | const blob = await Packer.toBlob(doc); |
| | const url = URL.createObjectURL(blob); |
| | const link = document.createElement('a'); |
| | link.href = url; |
| | link.download = filename; |
| | document.body.appendChild(link); |
| | link.click(); |
| | document.body.removeChild(link); |
| | URL.revokeObjectURL(url); |
| | } |
| |
|
| | function renderQuestion(question: GeneratedQuestion, questionNumber: number, includeAnswers: boolean = true): Paragraph[] { |
| | const elements: Paragraph[] = []; |
| | |
| | |
| | elements.push( |
| | new Paragraph({ |
| | children: [ |
| | new TextRun({ |
| | text: `Question ${questionNumber}`, |
| | bold: true, |
| | size: 24 |
| | }), |
| | new TextRun({ |
| | text: ` (${question.type})`, |
| | size: 20, |
| | color: '666666' |
| | }) |
| | ], |
| | spacing: { |
| | before: 300, |
| | after: 150 |
| | } |
| | }) |
| | ); |
| | |
| | |
| | elements.push( |
| | new Paragraph({ |
| | children: [ |
| | new TextRun({ |
| | text: question.stem, |
| | size: 22 |
| | }) |
| | ], |
| | spacing: { |
| | after: 200 |
| | } |
| | }) |
| | ); |
| | |
| | |
| | elements.push( |
| | new Paragraph({ |
| | children: [ |
| | new TextRun({ |
| | text: `Points: ${question.points}`, |
| | size: 18, |
| | italics: true, |
| | color: '666666' |
| | }) |
| | ], |
| | spacing: { |
| | after: 150 |
| | } |
| | }) |
| | ); |
| | |
| | |
| | elements.push(...renderQuestionContent(question, includeAnswers)); |
| | |
| | |
| | elements.push( |
| | new Paragraph({ |
| | children: [ |
| | new TextRun({ |
| | text: '─'.repeat(50), |
| | color: 'CCCCCC' |
| | }) |
| | ], |
| | spacing: { |
| | before: 200, |
| | after: 200 |
| | } |
| | }) |
| | ); |
| | |
| | return elements; |
| | } |
| |
|
| | function renderQuestionContent(question: GeneratedQuestion, includeAnswers: boolean = true): Paragraph[] { |
| | const elements: Paragraph[] = []; |
| | |
| | |
| | if (question.content && typeof question.content === 'object') { |
| | |
| | if ('Options' in question.content && question.content.Options) { |
| | |
| | const options = question.content.Options as { A: string; B: string; C: string; D: string; }; |
| | const correctAnswer = (question.content as { Answer?: string }).Answer; |
| | |
| | Object.entries(options).forEach(([key, value]) => { |
| | const isCorrect = key === correctAnswer; |
| | |
| | elements.push( |
| | new Paragraph({ |
| | children: [ |
| | new TextRun({ |
| | text: `${key}. ${value}`, |
| | size: 20, |
| | color: includeAnswers && isCorrect ? '008000' : '000000', |
| | bold: includeAnswers && isCorrect |
| | }) |
| | ], |
| | spacing: { |
| | after: 100 |
| | } |
| | }) |
| | ); |
| | }); |
| | |
| | |
| | if (includeAnswers && correctAnswer) { |
| | elements.push( |
| | new Paragraph({ |
| | children: [ |
| | new TextRun({ |
| | text: `Answer: ${correctAnswer}`, |
| | bold: true, |
| | size: 20, |
| | color: '008000' |
| | }) |
| | ], |
| | spacing: { |
| | before: 200, |
| | after: 200 |
| | } |
| | }) |
| | ); |
| | } |
| | } |
| | } |
| | |
| | return elements; |
| | } |
| |
|
| | export async function exportQuestionsToJson(questions: GeneratedQuestion[], filename: string = 'quiz-questions.json') { |
| | const data = { |
| | metadata: { |
| | exportDate: new Date().toISOString(), |
| | totalQuestions: questions.length, |
| | totalPoints: questions.reduce((sum, q) => sum + q.points, 0) |
| | }, |
| | questions: questions.map(q => ({ |
| | id: q.id, |
| | type: q.type, |
| | stem: q.stem, |
| | content: q.content, |
| | points: q.points, |
| | createdAt: q.createdAt |
| | })) |
| | }; |
| | |
| | const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); |
| | const url = URL.createObjectURL(blob); |
| | const link = document.createElement('a'); |
| | link.href = url; |
| | link.download = filename; |
| | document.body.appendChild(link); |
| | link.click(); |
| | document.body.removeChild(link); |
| | URL.revokeObjectURL(url); |
| | } |
| |
|
| | export async function exportQuestionsToMarkdown( |
| | questions: GeneratedQuestion[], |
| | filename: string = 'quiz-questions.md', |
| | includeAnswers: boolean = true |
| | ) { |
| | let markdown = '# Quiz Questions\n\n'; |
| | markdown += `Generated: ${new Date().toLocaleDateString()}\n\n`; |
| | markdown += `Total Questions: ${questions.length}\n\n`; |
| | markdown += '---\n\n'; |
| | |
| | questions.forEach((question, index) => { |
| | markdown += `## Question ${index + 1} (${question.type})\n\n`; |
| | markdown += `**Points:** ${question.points}\n\n`; |
| | markdown += `${question.stem}\n\n`; |
| | |
| | if (question.content && typeof question.content === 'object') { |
| | if ('Options' in question.content && question.content.Options) { |
| | const options = question.content.Options as { A: string; B: string; C: string; D: string; }; |
| | const correctAnswer = (question.content as { Answer?: string }).Answer; |
| | |
| | Object.entries(options).forEach(([key, value]) => { |
| | const isCorrect = includeAnswers && key === correctAnswer; |
| | markdown += `${key}. ${value}${isCorrect ? ' ✓' : ''}\n`; |
| | }); |
| | |
| | if (includeAnswers && correctAnswer) { |
| | markdown += `\n**Answer:** ${correctAnswer}\n`; |
| | } |
| | } |
| | } |
| | |
| | markdown += '\n---\n\n'; |
| | }); |
| | |
| | const blob = new Blob([markdown], { type: 'text/markdown' }); |
| | const url = URL.createObjectURL(blob); |
| | const link = document.createElement('a'); |
| | link.href = url; |
| | link.download = filename; |
| | document.body.appendChild(link); |
| | link.click(); |
| | document.body.removeChild(link); |
| | URL.revokeObjectURL(url); |
| | } |
| |
|
| | export async function exportQuestionsToCsv(questions: GeneratedQuestion[], filename: string = 'quiz-questions.csv') { |
| | |
| | let csv = 'Question Number,Type,Points,Question,Option A,Option B,Option C,Option D,Correct Answer\n'; |
| | |
| | questions.forEach((question, index) => { |
| | const row = []; |
| | row.push(index + 1); |
| | row.push(question.type); |
| | row.push(question.points); |
| | row.push(`"${question.stem.replace(/"/g, '""')}"`); |
| | |
| | if (question.content && typeof question.content === 'object' && 'Options' in question.content) { |
| | const options = question.content.Options as { A: string; B: string; C: string; D: string; }; |
| | const correctAnswer = (question.content as { Answer?: string }).Answer; |
| | |
| | row.push(`"${options.A.replace(/"/g, '""')}"`); |
| | row.push(`"${options.B.replace(/"/g, '""')}"`); |
| | row.push(`"${options.C.replace(/"/g, '""')}"`); |
| | row.push(`"${options.D.replace(/"/g, '""')}"`); |
| | row.push(correctAnswer || ''); |
| | } else { |
| | // Add empty cells for non-multiple choice questions |
| | row.push('', '', '', '', ''); |
| | } |
| | |
| | csv += row.join(',') + '\n'; |
| | }); |
| | |
| | const blob = new Blob([csv], { type: 'text/csv' }); |
| | const url = URL.createObjectURL(blob); |
| | const link = document.createElement('a'); |
| | link.href = url; |
| | link.download = filename; |
| | document.body.appendChild(link); |
| | link.click(); |
| | document.body.removeChild(link); |
| | URL.revokeObjectURL(url); |
| | } |