Fix 'Review Answer' button in Quiz doing nothing & improvements to quiz
This commit is contained in:
304
server.js
304
server.js
@@ -1281,63 +1281,123 @@ app.post('/api/generate-quiz', requireAuth, async (req, res) => {
|
||||
|
||||
|
||||
|
||||
// Call Flowise API
|
||||
const response = await axios.post(`${FLOWISE_API_URL}/${FLOWISE_CHATFLOW_ID}`, {
|
||||
question: prompt,
|
||||
history: []
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Call Flowise API with retry logic for better AI responses
|
||||
let quizData;
|
||||
try {
|
||||
const responseText = response.data.text || response.data.answer || response.data;
|
||||
let retryCount = 0;
|
||||
const maxRetries = 2;
|
||||
|
||||
while (retryCount <= maxRetries) {
|
||||
try {
|
||||
const response = await axios.post(`${FLOWISE_API_URL}/${FLOWISE_CHATFLOW_ID}`, {
|
||||
question: retryCount > 0 ?
|
||||
`${prompt}\n\nIMPORTANT: Please provide ONLY a valid JSON array of questions. Do not include any explanatory text, greetings, or additional commentary. Start your response directly with [ and end with ].` :
|
||||
prompt,
|
||||
history: []
|
||||
});
|
||||
|
||||
|
||||
// Try to extract JSON from the response
|
||||
let jsonString = null;
|
||||
|
||||
// First, try to find JSON wrapped in code blocks
|
||||
const codeBlockMatch = responseText.match(/```(?:json)?\s*(\[[\s\S]*?\])\s*```/);
|
||||
if (codeBlockMatch) {
|
||||
jsonString = codeBlockMatch[1];
|
||||
const responseText = response.data.text || response.data.answer || response.data;
|
||||
|
||||
} else {
|
||||
// Try to find JSON array by counting brackets
|
||||
const startIndex = responseText.indexOf('[');
|
||||
if (startIndex !== -1) {
|
||||
let bracketCount = 0;
|
||||
let endIndex = startIndex;
|
||||
// Check if the AI response looks like it's not following instructions
|
||||
if (responseText.toLowerCase().includes('do you have a question') ||
|
||||
responseText.toLowerCase().includes('would you like me to help') ||
|
||||
responseText.toLowerCase().includes('something else related') ||
|
||||
responseText.toLowerCase().includes('how can i help') ||
|
||||
(!responseText.includes('[') && !responseText.includes('{'))) {
|
||||
|
||||
for (let i = startIndex; i < responseText.length; i++) {
|
||||
if (responseText[i] === '[') bracketCount++;
|
||||
if (responseText[i] === ']') bracketCount--;
|
||||
if (bracketCount === 0) {
|
||||
endIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bracketCount === 0) {
|
||||
jsonString = responseText.substring(startIndex, endIndex + 1);
|
||||
|
||||
if (retryCount < maxRetries) {
|
||||
retryCount++;
|
||||
console.log(`AI gave improper response, retrying... (attempt ${retryCount + 1})`);
|
||||
continue;
|
||||
} else {
|
||||
throw new Error('The AI is not responding properly to quiz generation requests. Please try again with a more specific topic or try again later.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonString) {
|
||||
|
||||
// Try to extract JSON from the response
|
||||
let jsonString = null;
|
||||
|
||||
// First, try to find JSON wrapped in code blocks
|
||||
const codeBlockMatch = responseText.match(/```(?:json)?\s*(\[[\s\S]*?\])\s*```/);
|
||||
if (codeBlockMatch) {
|
||||
jsonString = codeBlockMatch[1];
|
||||
} else {
|
||||
// Try to find JSON array by counting brackets
|
||||
const startIndex = responseText.indexOf('[');
|
||||
if (startIndex !== -1) {
|
||||
let bracketCount = 0;
|
||||
let endIndex = startIndex;
|
||||
|
||||
for (let i = startIndex; i < responseText.length; i++) {
|
||||
if (responseText[i] === '[') bracketCount++;
|
||||
if (responseText[i] === ']') bracketCount--;
|
||||
if (bracketCount === 0) {
|
||||
endIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bracketCount === 0) {
|
||||
jsonString = responseText.substring(startIndex, endIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!jsonString) {
|
||||
if (retryCount < maxRetries) {
|
||||
retryCount++;
|
||||
console.log(`Could not find JSON in response, retrying... (attempt ${retryCount + 1})`);
|
||||
continue;
|
||||
} else {
|
||||
throw new Error('Could not find valid JSON in the AI response after multiple attempts. Please try generating the quiz again.');
|
||||
}
|
||||
}
|
||||
|
||||
quizData = JSON.parse(jsonString);
|
||||
} else {
|
||||
quizData = generateFallbackQuiz(topic, questionCount, quizType);
|
||||
|
||||
// Validate the parsed data
|
||||
if (!Array.isArray(quizData) || quizData.length === 0) {
|
||||
if (retryCount < maxRetries) {
|
||||
retryCount++;
|
||||
console.log(`Invalid quiz format received, retrying... (attempt ${retryCount + 1})`);
|
||||
continue;
|
||||
} else {
|
||||
throw new Error('The AI generated an invalid quiz format after multiple attempts. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
// Validate each question has required fields
|
||||
for (let i = 0; i < quizData.length; i++) {
|
||||
const q = quizData[i];
|
||||
if (!q.question || (!q.correct && !q.answer)) {
|
||||
if (retryCount < maxRetries) {
|
||||
retryCount++;
|
||||
console.log(`Question ${i + 1} missing required fields, retrying... (attempt ${retryCount + 1})`);
|
||||
continue;
|
||||
} else {
|
||||
throw new Error(`Question ${i + 1} is missing required fields after multiple attempts. Please try generating the quiz again.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, the quiz is valid
|
||||
break;
|
||||
|
||||
} catch (parseError) {
|
||||
if (retryCount < maxRetries) {
|
||||
retryCount++;
|
||||
console.log(`Parse error occurred, retrying... (attempt ${retryCount + 1}):`, parseError.message);
|
||||
continue;
|
||||
} else {
|
||||
console.error('Quiz parsing error after retries:', parseError);
|
||||
console.error('AI Response was:', response?.data?.text || response?.data?.answer || response?.data || 'No response');
|
||||
|
||||
// Return error instead of fallback
|
||||
return res.json({
|
||||
success: false,
|
||||
error: parseError.message || 'Failed to parse quiz questions from AI response after multiple attempts. Please try again with a different topic or rephrase your request.'
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (parseError) {
|
||||
console.error('Quiz parsing error:', parseError);
|
||||
quizData = generateFallbackQuiz(topic, questionCount, quizType);
|
||||
}
|
||||
|
||||
// Ensure quizData is always defined and is an array
|
||||
if (!quizData || !Array.isArray(quizData) || quizData.length === 0) {
|
||||
quizData = generateFallbackQuiz(topic, questionCount, quizType);
|
||||
}
|
||||
|
||||
|
||||
@@ -1354,154 +1414,14 @@ app.post('/api/generate-quiz', requireAuth, async (req, res) => {
|
||||
console.error('Quiz generation error:', error);
|
||||
console.error('Error stack:', error.stack);
|
||||
|
||||
// Return fallback quiz on error
|
||||
const fallbackQuiz = generateFallbackQuiz(req.body.topic || 'General Knowledge', req.body.questionCount || 5, req.body.quizType || 'multiple-choice');
|
||||
// Return error instead of fallback quiz
|
||||
res.json({
|
||||
success: true,
|
||||
quiz: fallbackQuiz,
|
||||
topic: req.body.topic || 'General Knowledge',
|
||||
difficulty: req.body.difficulty || 'beginner',
|
||||
questionCount: req.body.questionCount || 5,
|
||||
quizType: req.body.quizType || 'multiple-choice'
|
||||
success: false,
|
||||
error: 'Failed to generate quiz. Please check your connection and try again, or try a different topic.'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function generateFallbackQuiz(topic, questionCount, quizType) {
|
||||
const questions = [];
|
||||
|
||||
// Generate actual questions based on topic
|
||||
const topicQuestions = {
|
||||
'javascript': [
|
||||
{
|
||||
question: 'What is the correct way to declare a variable in JavaScript?',
|
||||
options: ['A) var myVar = 5;', 'B) variable myVar = 5;', 'C) declare myVar = 5;', 'D) int myVar = 5;'],
|
||||
correct: 'A',
|
||||
explanation: 'The var keyword is used to declare variables in JavaScript.'
|
||||
},
|
||||
{
|
||||
question: 'Which method is used to add an element to the end of an array?',
|
||||
options: ['A) push()', 'B) add()', 'C) append()', 'D) insert()'],
|
||||
correct: 'A',
|
||||
explanation: 'The push() method adds one or more elements to the end of an array.'
|
||||
},
|
||||
{
|
||||
question: 'What does === operator do in JavaScript?',
|
||||
options: ['A) Assigns a value', 'B) Compares values only', 'C) Compares values and types', 'D) Declares a constant'],
|
||||
correct: 'C',
|
||||
explanation: 'The === operator compares both value and type without type conversion.'
|
||||
},
|
||||
{
|
||||
question: 'How do you write a comment in JavaScript?',
|
||||
options: ['A) <!-- comment -->', 'B) // comment', 'C) # comment', 'D) /* comment */'],
|
||||
correct: 'B',
|
||||
explanation: 'Single-line comments in JavaScript start with //.'
|
||||
},
|
||||
{
|
||||
question: 'What is the result of typeof null in JavaScript?',
|
||||
options: ['A) "null"', 'B) "undefined"', 'C) "object"', 'D) "boolean"'],
|
||||
correct: 'C',
|
||||
explanation: 'typeof null returns "object" due to a legacy bug in JavaScript.'
|
||||
}
|
||||
],
|
||||
'python': [
|
||||
{
|
||||
question: 'Which keyword is used to create a function in Python?',
|
||||
options: ['A) function', 'B) def', 'C) create', 'D) func'],
|
||||
correct: 'B',
|
||||
explanation: 'The def keyword is used to define functions in Python.'
|
||||
},
|
||||
{
|
||||
question: 'What is the correct way to create a list in Python?',
|
||||
options: ['A) list = (1, 2, 3)', 'B) list = {1, 2, 3}', 'C) list = [1, 2, 3]', 'D) list = <1, 2, 3>'],
|
||||
correct: 'C',
|
||||
explanation: 'Square brackets [] are used to create lists in Python.'
|
||||
},
|
||||
{
|
||||
question: 'How do you start a comment in Python?',
|
||||
options: ['A) //', 'B) #', 'C) /*', 'D) --'],
|
||||
correct: 'B',
|
||||
explanation: 'Comments in Python start with the # symbol.'
|
||||
},
|
||||
{
|
||||
question: 'What does len() function do in Python?',
|
||||
options: ['A) Returns the length of an object', 'B) Converts to lowercase', 'C) Rounds a number', 'D) Prints output'],
|
||||
correct: 'A',
|
||||
explanation: 'The len() function returns the number of items in an object.'
|
||||
},
|
||||
{
|
||||
question: 'Which of the following is a mutable data type in Python?',
|
||||
options: ['A) tuple', 'B) string', 'C) list', 'D) integer'],
|
||||
correct: 'C',
|
||||
explanation: 'Lists are mutable, meaning they can be changed after creation.'
|
||||
}
|
||||
],
|
||||
'math': [
|
||||
{
|
||||
question: 'What is the value of π (pi) approximately?',
|
||||
options: ['A) 3.14159', 'B) 2.71828', 'C) 1.61803', 'D) 4.66920'],
|
||||
correct: 'A',
|
||||
explanation: 'π (pi) is approximately 3.14159, the ratio of circumference to diameter.'
|
||||
},
|
||||
{
|
||||
question: 'What is the derivative of x²?',
|
||||
options: ['A) x', 'B) 2x', 'C) x³', 'D) 2x²'],
|
||||
correct: 'B',
|
||||
explanation: 'Using the power rule, the derivative of x² is 2x.'
|
||||
},
|
||||
{
|
||||
question: 'What is the Pythagorean theorem?',
|
||||
options: ['A) a + b = c', 'B) a² + b² = c²', 'C) a × b = c', 'D) a² - b² = c²'],
|
||||
correct: 'B',
|
||||
explanation: 'The Pythagorean theorem states that a² + b² = c² for right triangles.'
|
||||
},
|
||||
{
|
||||
question: 'What is the factorial of 5?',
|
||||
options: ['A) 25', 'B) 120', 'C) 60', 'D) 100'],
|
||||
correct: 'B',
|
||||
explanation: '5! = 5 × 4 × 3 × 2 × 1 = 120'
|
||||
},
|
||||
{
|
||||
question: 'What is the square root of 64?',
|
||||
options: ['A) 6', 'B) 7', 'C) 8', 'D) 9'],
|
||||
correct: 'C',
|
||||
explanation: 'The square root of 64 is 8 because 8² = 64.'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Get appropriate questions for the topic
|
||||
const availableQuestions = topicQuestions[topic.toLowerCase()] || topicQuestions['math'];
|
||||
|
||||
for (let i = 0; i < questionCount; i++) {
|
||||
const questionIndex = i % availableQuestions.length;
|
||||
const baseQuestion = availableQuestions[questionIndex];
|
||||
|
||||
if (quizType === 'multiple-choice') {
|
||||
questions.push({
|
||||
question: baseQuestion.question,
|
||||
options: baseQuestion.options,
|
||||
correct: baseQuestion.correct,
|
||||
explanation: baseQuestion.explanation
|
||||
});
|
||||
} else if (quizType === 'true-false') {
|
||||
questions.push({
|
||||
question: baseQuestion.question.replace(/Which|What|How/, 'Is it true that'),
|
||||
correct: Math.random() > 0.5 ? 'True' : 'False',
|
||||
explanation: baseQuestion.explanation
|
||||
});
|
||||
} else {
|
||||
questions.push({
|
||||
question: baseQuestion.question,
|
||||
answer: baseQuestion.correct,
|
||||
keywords: [baseQuestion.correct.toLowerCase(), topic.toLowerCase()]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return questions;
|
||||
}
|
||||
|
||||
app.post('/api/submit-quiz', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { answers, quiz, topic, difficulty, quizType } = req.body;
|
||||
|
||||
Reference in New Issue
Block a user