diff --git a/db/migrations/005_create_exams_table.sql b/db/migrations/005_create_exams_table.sql new file mode 100644 index 0000000..e11f43e --- /dev/null +++ b/db/migrations/005_create_exams_table.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS exams ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + created_by INT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/db/migrations/006_create_exam_questions_table.sql b/db/migrations/006_create_exam_questions_table.sql new file mode 100644 index 0000000..800b2bb --- /dev/null +++ b/db/migrations/006_create_exam_questions_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS exam_questions ( + id INT AUTO_INCREMENT PRIMARY KEY, + exam_id INT NOT NULL, + question_text TEXT NOT NULL, + question_type ENUM('multiple_choice', 'free_text') NOT NULL, + options JSON, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/db/migrations/007_create_student_exams_table.sql b/db/migrations/007_create_student_exams_table.sql new file mode 100644 index 0000000..44c2005 --- /dev/null +++ b/db/migrations/007_create_student_exams_table.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS student_exams ( + id INT AUTO_INCREMENT PRIMARY KEY, + exam_id INT NOT NULL, + student_id INT NOT NULL, + status ENUM('assigned', 'in-progress', 'completed') DEFAULT 'assigned', + score INT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (exam_id) REFERENCES exams(id) ON DELETE CASCADE, + FOREIGN KEY (student_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE KEY (exam_id, student_id) +); \ No newline at end of file diff --git a/db/migrations/008_create_student_answers_table.sql b/db/migrations/008_create_student_answers_table.sql new file mode 100644 index 0000000..ae220b8 --- /dev/null +++ b/db/migrations/008_create_student_answers_table.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS student_answers ( + id INT AUTO_INCREMENT PRIMARY KEY, + student_exam_id INT NOT NULL, + question_id INT NOT NULL, + answer_text TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (student_exam_id) REFERENCES student_exams(id) ON DELETE CASCADE, + FOREIGN KEY (question_id) REFERENCES exam_questions(id) ON DELETE CASCADE, + UNIQUE KEY (student_exam_id, question_id) +); \ No newline at end of file diff --git a/exam_questions.php b/exam_questions.php new file mode 100644 index 0000000..1802de8 --- /dev/null +++ b/exam_questions.php @@ -0,0 +1,180 @@ +prepare('SELECT * FROM exams WHERE id = ? AND created_by = ?'); +$stmt->execute([$exam_id, $user_id]); +$exam = $stmt->fetch(); + +if (!$exam) { + echo "Exam not found or you don't have permission to edit it."; + exit(); +} + +// Handle question form submission +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['question_text'])) { + $question_text = trim($_POST['question_text']); + $question_type = $_POST['question_type']; + $options = null; + + if ($question_type === 'multiple_choice') { + $options_raw = trim($_POST['options']); + if (!empty($options_raw)) { + $options = json_encode(array_map('trim', explode("\n", $options_raw))); + } + } + + if (!empty($question_text)) { + if (isset($_POST['question_id']) && !empty($_POST['question_id'])) { + // Update question + $stmt = $pdo->prepare('UPDATE exam_questions SET question_text = ?, question_type = ?, options = ? WHERE id = ? AND exam_id = ?'); + $stmt->execute([$question_text, $question_type, $options, $_POST['question_id'], $exam_id]); + } else { + // Add new question + $stmt = $pdo->prepare('INSERT INTO exam_questions (exam_id, question_text, question_type, options) VALUES (?, ?, ?, ?)'); + $stmt->execute([$exam_id, $question_text, $question_type, $options]); + } + } + header('Location: exam_questions.php?exam_id=' . $exam_id); + exit(); +} + +// Handle question deletion +if (isset($_GET['delete_question'])) { + $question_id = $_GET['delete_question']; + $stmt = $pdo->prepare('DELETE FROM exam_questions WHERE id = ? AND exam_id = ?'); + $stmt->execute([$question_id, $exam_id]); + header('Location: exam_questions.php?exam_id=' . $exam_id); + exit(); +} + +// Fetch questions for the exam +$stmt = $pdo->prepare('SELECT * FROM exam_questions WHERE exam_id = ? ORDER BY created_at ASC'); +$stmt->execute([$exam_id]); +$questions = $stmt->fetchAll(); + +// Check if we are editing a question +$edit_question = null; +if (isset($_GET['edit_question'])) { + $stmt = $pdo->prepare('SELECT * FROM exam_questions WHERE id = ? AND exam_id = ?'); + $stmt->execute([$_GET['edit_question'], $exam_id]); + $edit_question = $stmt->fetch(); +} + +?> + + + + + + Manage Questions for <?php echo htmlspecialchars($exam['name']); ?> + + + +
+

Manage Questions for ""

+ +
+
+
+

Question

+
+ + + +
+ + +
+
+ + +
+
+ + +
+ + + Cancel Edit + +
+
+ +
+

Questions

+ + + + + + + + + + + + + + + + + + + + + + +
QuestionTypeActions
+ Edit | + Delete +
No questions have been added to this exam yet.
+
+
+ + + diff --git a/exams.php b/exams.php index 4bed414..5fe7531 100644 --- a/exams.php +++ b/exams.php @@ -1,32 +1,177 @@ Dashboard'; + +// Role-based logic +if ($role === 'teacher') { + $page_title = 'Manage Exams'; + + // Handle form submissions for creating/editing exams + if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['exam_name'])) { + $exam_name = trim($_POST['exam_name']); + if (!empty($exam_name)) { + if (isset($_POST['exam_id']) && !empty($_POST['exam_id'])) { + $stmt = $pdo->prepare('UPDATE exams SET name = ? WHERE id = ? AND created_by = ?'); + $stmt->execute([$exam_name, $_POST['exam_id'], $user_id]); + } else { + $stmt = $pdo->prepare('INSERT INTO exams (name, created_by) VALUES (?, ?)'); + $stmt->execute([$exam_name, $user_id]); + } + } + header('Location: exams.php'); + exit(); + } + + // Handle exam deletion + if (isset($_GET['delete_exam'])) { + $stmt = $pdo->prepare('DELETE FROM exams WHERE id = ? AND created_by = ?'); + $stmt->execute([$_GET['delete_exam'], $user_id]); + header('Location: exams.php'); + exit(); + } + + // Fetch exams for the teacher view + $stmt = $pdo->prepare('SELECT * FROM exams WHERE created_by = ? ORDER BY created_at DESC'); + $stmt->execute([$user_id]); + $exams = $stmt->fetchAll(); + + // Check if we are editing an exam + $edit_exam = null; + if (isset($_GET['edit_exam'])) { + $stmt = $pdo->prepare('SELECT * FROM exams WHERE id = ? AND created_by = ?'); + $stmt->execute([$_GET['edit_exam'], $user_id]); + $edit_exam = $stmt->fetch(); + } + +} elseif ($role === 'student') { + $page_title = 'Your Exams'; + + // Fetch assigned exams for the student view + $stmt = $pdo->prepare(' + SELECT e.name, se.status, se.score, se.id as student_exam_id + FROM student_exams se + JOIN exams e ON se.exam_id = e.id + WHERE se.student_id = ? + ORDER BY e.created_at DESC + '); + $stmt->execute([$user_id]); + $assigned_exams = $stmt->fetchAll(); + +} else { + // Redirect other roles to their dashboard + header('Location: ' . $role . '_dashboard.php'); + exit(); +} ?> - Exams - + <?php echo $page_title; ?> +
-

Exams

+

-

Exam Management

-

This page will contain student exam information.

+ +
+

Exam

+
+ + + +
+ + +
+ + + Cancel Edit + +
+
+
+

Your Exams

+ + + + + + + + + + + + + + + + + + +
Exam NameActions
+ View Submissions | + Edit | + Manage Questions | + Delete +
You have not created any exams yet.
+
+ +
+

Assigned Exams

+ + + + + + + + + + + + + + + + + + + + + + +
Exam NameStatusScoreAction
+ + Take Exam + + View Results + +
You have no assigned exams.
+
+
\ No newline at end of file diff --git a/take_exam.php b/take_exam.php new file mode 100644 index 0000000..d5e900a --- /dev/null +++ b/take_exam.php @@ -0,0 +1,133 @@ +prepare('SELECT * FROM student_exams WHERE id = ? AND student_id = ?'); +$stmt->execute([$student_exam_id, $user_id]); +$student_exam = $stmt->fetch(); + +if (!$student_exam) { + echo "You are not assigned to this exam."; + exit(); +} + +// Prevent re-taking a completed exam +if ($student_exam['status'] === 'completed') { + echo "You have already completed this exam."; + // Maybe redirect to a results page in the future + echo '
Back to Exams'; + exit(); +} + +// Fetch exam details +$stmt = $pdo->prepare('SELECT * FROM exams WHERE id = ?'); +$stmt->execute([$student_exam['exam_id']]); +$exam = $stmt->fetch(); + +// Fetch exam questions +$stmt = $pdo->prepare('SELECT * FROM exam_questions WHERE exam_id = ? ORDER BY id ASC'); +$stmt->execute([$exam['id']]); +$questions = $stmt->fetchAll(); + +// Handle exam submission +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['answers'])) { + $answers = $_POST['answers']; + + $pdo->beginTransaction(); + try { + foreach ($answers as $question_id => $answer_text) { + // Use INSERT ... ON DUPLICATE KEY UPDATE to prevent duplicate answer submissions + $sql = 'INSERT INTO student_answers (student_exam_id, question_id, answer_text) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE answer_text = VALUES(answer_text)'; + $stmt = $pdo->prepare($sql); + $stmt->execute([$student_exam_id, $question_id, trim($answer_text)]); + } + + // Mark exam as completed + $stmt = $pdo->prepare('UPDATE student_exams SET status = \'completed\' WHERE id = ?'); + $stmt->execute([$student_exam_id]); + + $pdo->commit(); + + header('Location: exams.php'); + exit(); + + } catch (Exception $e) { + $pdo->rollBack(); + // Log error properly in a real application + die("An error occurred while submitting your exam. Please try again. Error: " . $e->getMessage()); + } +} + +// If student is starting the exam, mark it as 'in-progress' +if ($student_exam['status'] === 'assigned') { + $stmt = $pdo->prepare('UPDATE student_exams SET status = \'in-progress\' WHERE id = ?'); + $stmt->execute([$student_exam_id]); +} + +?> + + + + + + Take Exam: <?php echo htmlspecialchars($exam['name']); ?> + + + +
+

+ +
+
+
+ $q): ?> +
+

Question :

+ + + + +
+ + +
+ + + + + +
+ + + +

This exam has no questions.

+ + + +
+
+ +