Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>SmartLearn - Quiz Generation</title> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap'); | |
body { | |
font-family: 'Poppins', sans-serif; | |
margin: 0; | |
padding: 0; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
min-height: 100vh; | |
background-image: url('../static/quiz.jpg'); | |
background-size: cover; | |
background-position: center; | |
background-attachment: fixed; | |
} | |
.container { | |
border-radius: 20px; | |
padding: 40px; | |
width: 90%; | |
max-width: 500px; | |
text-align: center; | |
position: relative; | |
z-index: 2; | |
background: rgba(255, 255, 255, 0.1); | |
backdrop-filter: blur(10px); | |
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); | |
border: 1px solid rgba(255, 255, 255, 0.18); | |
} | |
.quiz-form { | |
padding: 20px; | |
border-radius: 10px; | |
} | |
.form-group { | |
margin-bottom: 20px; | |
text-align: left; | |
} | |
.form-group label { | |
display: block; | |
margin-bottom: 5px; | |
color: #fff; | |
font-weight: 600; | |
} | |
.form-group input, | |
.form-group select { | |
width: 100%; | |
padding: 10px; | |
border: none; | |
border-radius: 5px; | |
background: rgba(255, 255, 255, 0.8); | |
color: #000; | |
font-size: 16px; | |
} | |
.form-group select { | |
appearance: none; | |
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); | |
background-repeat: no-repeat; | |
background-position: right 10px center; | |
background-size: 20px; | |
} | |
.btn { | |
background-color: #3498db; | |
color: white; | |
border: none; | |
padding: 12px 24px; | |
border-radius: 30px; | |
cursor: pointer; | |
font-size: 18px; | |
transition: all 0.3s ease; | |
} | |
.btn:hover { | |
background-color: #2980b9; | |
transform: translateY(-2px); | |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); | |
} | |
h1, h2, h3 { | |
color: #fff; | |
text-shadow: 2px 2px 4px rgba(0,0,0,0.5); | |
} | |
.score-circle { | |
width: 150px; | |
height: 150px; | |
background-color: rgba(52, 152, 219, 0.8); | |
border-radius: 50%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
margin: 0 auto 30px; | |
font-size: 36px; | |
font-weight: bold; | |
color: white; | |
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.3); | |
} | |
.areas-to-improve { | |
background-color: rgba(248, 249, 250, 0.1); | |
border-radius: 10px; | |
padding: 20px; | |
text-align: left; | |
color: #fff; | |
} | |
.areas-to-improve ol { | |
padding-left: 20px; | |
} | |
.areas-to-improve li { | |
margin-bottom: 10px; | |
} | |
.validation-container { | |
display: none; | |
justify-content: center; | |
align-items: center; | |
flex-direction: column; | |
} | |
.loader { | |
border: 5px solid rgba(255, 255, 255, 0.3); | |
border-top: 5px solid #3498db; | |
border-radius: 50%; | |
width: 50px; | |
height: 50px; | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.validation-text { | |
margin-top: 20px; | |
font-size: 18px; | |
color: #fff; | |
} | |
.correct { color: #2ecc71; } | |
.incorrect { color: #e74c3c; } | |
#areas-to-improve { margin-top: 20px; } | |
.options li { | |
display: flex; | |
align-items: center; | |
margin-bottom: 10px; | |
color: #fff; | |
} | |
.options li input { | |
margin-right: 10px; | |
} | |
.back-button { | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
background-color: #6c757d; | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
border-radius: 30px; | |
cursor: pointer; | |
transition: background-color 0.3s; | |
font-size: 16px; | |
} | |
.back-button:hover { | |
background-color: #5a6268; | |
} | |
.fade-in { | |
animation: fadeIn 0.5s ease-in-out; | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(20px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.icon { | |
margin-right: 10px; | |
} | |
.loading-animation { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
margin-top: 20px; | |
} | |
.loading-animation p { | |
margin-top: 10px; | |
color: #fff; | |
font-weight: 600; | |
} | |
@keyframes pulse { | |
0% { transform: scale(1); } | |
50% { transform: scale(1.1); } | |
100% { transform: scale(1); } | |
} | |
.loading-icon { | |
font-size: 48px; | |
color: #3498db; | |
animation: pulse 1.5s infinite; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container fade-in"> | |
<button class="back-button" onclick="goBack()"><i class="fas fa-arrow-left"></i> Back</button> | |
<h1><i class="fas fa-graduation-cap icon"></i>Generate Your Custom Quiz</h1> | |
<form class="quiz-form"> | |
<div class="form-group"> | |
<label for="subject"><i class="fas fa-book icon"></i>Subject:</label> | |
<input type="text" id="subject" name="subject" required> | |
</div> | |
<div class="form-group"> | |
<label for="topic"><i class="fas fa-lightbulb icon"></i>Topic:</label> | |
<input type="text" id="topic" name="topic" required> | |
</div> | |
<div class="form-group"> | |
<label for="num-questions"><i class="fas fa-list-ol icon"></i>Number of Questions:</label> | |
<select id="num-questions" name="num-questions"> | |
<option value="2">2</option> | |
<option value="5">5</option> | |
<option value="10">10</option> | |
<option value="15">15</option> | |
<option value="20">20</option> | |
</select> | |
</div> | |
<div class="form-group"> | |
<label for="difficulty"><i class="fas fa-signal icon"></i>Difficulty Level:</label> | |
<select id="difficulty" name="difficulty"> | |
<option value="easy">Easy</option> | |
<option value="medium">Medium</option> | |
<option value="hard">Hard</option> | |
</select> | |
</div> | |
<button type="submit" class="btn"><i class="fas fa-magic icon"></i>Generate Quiz</button> | |
</form> | |
<div id="loading-animation" class="loading-animation" style="display: none;"> | |
<i class="fas fa-cog loading-icon"></i> | |
<p id="loading-text">Generating Quiz...</p> | |
</div> | |
<div id="quiz-result" class="quiz-result" style="display: none;"> | |
<!-- Quiz questions will be dynamically inserted here --> | |
</div> | |
<div id="validation-container" class="validation-container"> | |
<div id="loader" class="loader"></div> | |
<div id="validation-text" class="validation-text">Validating...</div> | |
</div> | |
<div id="quiz-score" style="display: none;"></div> | |
<div id="areas-to-improve" class="areas-to-improve" style="display: none;"></div> | |
</div> | |
<script> | |
let quizData = []; | |
let studentId = '[email protected]'; // Assume you set this variable with the logged-in student's ID. | |
document.querySelector('.quiz-form').addEventListener('submit', function(e) { | |
e.preventDefault(); | |
const formData = new FormData(this); | |
const data = { | |
subject: formData.get('subject'), | |
topic: formData.get('topic'), | |
'num-questions': formData.get('num-questions'), | |
difficulty: formData.get('difficulty') | |
}; | |
// Hide the form and show loading animation | |
this.style.display = 'none'; | |
document.getElementById('loading-animation').style.display = 'flex'; | |
let loadingTexts = [ | |
"Crafting challenging questions...", | |
"Digging deep into the subject...", | |
"Polishing the perfect quiz for you...", | |
"Almost there, finalizing details..." | |
]; | |
let currentTextIndex = 0; | |
// Start the loading text animation | |
const loadingInterval = setInterval(() => { | |
document.getElementById('loading-text').textContent = loadingTexts[currentTextIndex]; | |
currentTextIndex = (currentTextIndex + 1) % loadingTexts.length; | |
}, 3000); | |
fetch('/generate_quiz', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(data) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Clear the loading interval | |
clearInterval(loadingInterval); | |
// Hide loading animation | |
document.getElementById('loading-animation').style.display = 'none'; | |
quizData = parseQuizData(data.quiz); | |
showQuiz(quizData); | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
// Clear the loading interval | |
clearInterval(loadingInterval); | |
// Hide loading animation and show error message | |
document.getElementById('loading-animation').style.display = 'none'; | |
alert('An error occurred while generating the quiz. Please try again.'); | |
}); | |
}); | |
function parseQuizData(quizString) { | |
const questions = quizString.split('Question:').filter(q => q.trim() !== ''); | |
return questions.map((question, index) => { | |
const [questionText, ...options] = question.split('\n').filter(line => line.trim() !== ''); | |
const answerLine = options.pop(); | |
const correctAnswer = parseInt(answerLine.split(':')[1].trim()); | |
return { | |
id: index + 1, | |
text: questionText.trim(), | |
options: options.map(opt => opt.replace('<<o>>', '').trim()), | |
correctAnswer: correctAnswer, | |
concept: questionText.match(/\[(.*?)\]/)[1] | |
}; | |
}); | |
} | |
function showQuiz(questions) { | |
const quizResult = document.getElementById('quiz-result'); | |
let quizHtml = '<h2>Your Custom Quiz</h2>'; | |
questions.forEach((question, index) => { | |
quizHtml += ` | |
<div class="question fade-in"> | |
<h3>${question.id}. ${question.text}</h3> | |
<ul class="options"> | |
`; | |
question.options.forEach((option, optionIndex) => { | |
quizHtml += ` | |
<li> | |
<input type="radio" name="q${index}" value="${optionIndex + 1}" id="q${index}o${optionIndex}"> | |
<label for="q${index}o${optionIndex}">${option}</label> | |
</li> | |
`; | |
}); | |
quizHtml += ` | |
</ul> | |
</div> | |
`; | |
}); | |
quizHtml += '<button onclick="submitQuiz()" class="btn">Submit Quiz</button>'; | |
quizResult.innerHTML = quizHtml; | |
quizResult.style.display = 'block'; | |
} | |
function submitQuiz() { | |
const validationContainer = document.getElementById('validation-container'); | |
const quizResult = document.getElementById('quiz-result'); | |
const quizScore = document.getElementById('quiz-score'); | |
const areasToImprove = document.getElementById('areas-to-improve'); | |
quizResult.style.display = 'none'; | |
validationContainer.style.display = 'flex'; | |
setTimeout(() => { | |
validationContainer.style.display = 'none'; | |
const score = calculateScore(); | |
displayResults(score); | |
// Get areas to improve | |
let improveAreas = []; | |
let incorrectQuestions = quizData.filter((question, index) => { | |
const selectedAnswer = document.querySelector(`input[name="q${index}"]:checked`); | |
return !selectedAnswer || parseInt(selectedAnswer.value) !== question.correctAnswer; | |
}); | |
if (incorrectQuestions.length > 0) { | |
improveAreas = incorrectQuestions.map(question => question.concept); | |
} | |
// Send areas to improve to the backend | |
fetch('/update_student_areas', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ student_id: studentId, areas_to_improve: improveAreas }) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.status === 'success') { | |
console.log('Student areas to improve updated successfully.'); | |
} else { | |
console.error('Failed to update student areas to improve.'); | |
} | |
}) | |
.catch(error => console.error('Error:', error)); | |
}, 2000); | |
} | |
function calculateScore() { | |
let score = 0; | |
quizData.forEach((question, index) => { | |
const selectedAnswer = document.querySelector(`input[name="q${index}"]:checked`); | |
if (selectedAnswer && parseInt(selectedAnswer.value) === question.correctAnswer) { | |
score++; | |
} | |
}); | |
return score; | |
} | |
function displayResults(score) { | |
const quizScore = document.getElementById('quiz-score'); | |
const areasToImprove = document.getElementById('areas-to-improve'); | |
quizScore.innerHTML = ` | |
<div class="score-circle">${score}/${quizData.length}</div> | |
<h3>You have scored</h3> | |
<p>Performance: ${getPerformanceEvaluation(score, quizData.length)}</p> | |
`; | |
quizScore.style.display = 'block'; | |
let incorrectQuestions = quizData.filter((question, index) => { | |
const selectedAnswer = document.querySelector(`input[name="q${index}"]:checked`); | |
return !selectedAnswer || parseInt(selectedAnswer.value) !== question.correctAnswer; | |
}); | |
if (incorrectQuestions.length > 0) { | |
let improveHtml = '<h3>Areas to be improved</h3><ol>'; | |
incorrectQuestions.forEach(question => { | |
improveHtml += `<li>${question.concept}</li>`; | |
}); | |
improveHtml += '</ol>'; | |
areasToImprove.innerHTML = improveHtml; | |
areasToImprove.style.display = 'block'; | |
} | |
} | |
function getPerformanceEvaluation(score, total) { | |
const percentage = (score / total) * 100; | |
if (percentage >= 80) return 'Excellent'; | |
if (percentage >= 60) return 'Good'; | |
if (percentage >= 40) return 'Average'; | |
return 'Needs Improvement'; | |
} | |
function goBack() { | |
window.history.back(); | |
} | |
</script> | |
</body> | |
</html> |