<?php
// Start session
session_start();
// Include database configuration
require_once '../config/database.php';
// Check if user is logged in and has admin role
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header('Location: login.php');
exit;
}
// Array to store results
$results = [];
$success = true;
// Tables we need to check/create
$tables = [
'exam_settings' => "
CREATE TABLE IF NOT EXISTS `exam_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`setting_key` varchar(100) NOT NULL,
`setting_value` text NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `setting_key` (`setting_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'question_banks' => "
CREATE TABLE IF NOT EXISTS `question_banks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`description` text,
`course_id` int(11) DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `course_id` (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'questions' => "
CREATE TABLE IF NOT EXISTS `questions` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`question_bank_id` int(11) NOT NULL,
`question_text` text NOT NULL,
`question_type` enum('multiple_choice','true_false','short_answer','essay') NOT NULL,
`difficulty` enum('easy','medium','hard') NOT NULL DEFAULT 'medium',
`marks` decimal(5,2) NOT NULL DEFAULT '1.00',
`is_practice` tinyint(1) NOT NULL DEFAULT '0',
`explanation` text,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `question_bank_id` (`question_bank_id`),
CONSTRAINT `questions_ibfk_1` FOREIGN KEY (`question_bank_id`) REFERENCES `question_banks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'question_options' => "
CREATE TABLE IF NOT EXISTS `question_options` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`question_id` int(11) NOT NULL,
`option_text` text NOT NULL,
`is_correct` tinyint(1) NOT NULL DEFAULT '0',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `question_id` (`question_id`),
CONSTRAINT `question_options_ibfk_1` FOREIGN KEY (`question_id`) REFERENCES `questions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'exam_schedules' => "
CREATE TABLE IF NOT EXISTS `exam_schedules` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`description` text,
`course_id` int(11) NOT NULL,
`exam_date` date NOT NULL,
`start_time` time NOT NULL,
`end_time` time NOT NULL,
`duration_minutes` int(11) NOT NULL,
`location` varchar(255) DEFAULT NULL,
`passing_percentage` decimal(5,2) NOT NULL DEFAULT '40.00',
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `course_id` (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'exam_question_maps' => "
CREATE TABLE IF NOT EXISTS `exam_question_maps` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`exam_id` int(11) NOT NULL,
`question_id` int(11) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `exam_question_unique` (`exam_id`,`question_id`),
KEY `question_id` (`question_id`),
CONSTRAINT `exam_question_maps_ibfk_1` FOREIGN KEY (`exam_id`) REFERENCES `exam_schedules` (`id`) ON DELETE CASCADE,
CONSTRAINT `exam_question_maps_ibfk_2` FOREIGN KEY (`question_id`) REFERENCES `questions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'student_exams' => "
CREATE TABLE IF NOT EXISTS `student_exams` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`exam_id` int(11) NOT NULL,
`status` enum('pending','in_progress','completed','graded','passed','failed') NOT NULL DEFAULT 'pending',
`start_time` datetime DEFAULT NULL,
`end_time` datetime DEFAULT NULL,
`total_score` decimal(10,2) DEFAULT NULL,
`percentage` decimal(5,2) DEFAULT NULL,
`attempt_number` int(11) DEFAULT '1',
`admin_remarks` text,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `exam_id` (`exam_id`),
CONSTRAINT `student_exams_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `student_exams_ibfk_2` FOREIGN KEY (`exam_id`) REFERENCES `exam_schedules` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'student_answers' => "
CREATE TABLE IF NOT EXISTS `student_answers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_exam_id` int(11) NOT NULL,
`question_id` int(11) NOT NULL,
`answer_text` text,
`selected_option_id` int(11) DEFAULT NULL,
`is_correct` tinyint(1) DEFAULT NULL,
`marks_obtained` decimal(5,2) DEFAULT NULL,
`admin_remarks` text,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `student_exam_id` (`student_exam_id`),
KEY `question_id` (`question_id`),
KEY `selected_option_id` (`selected_option_id`),
CONSTRAINT `student_answers_ibfk_1` FOREIGN KEY (`student_exam_id`) REFERENCES `student_exams` (`id`) ON DELETE CASCADE,
CONSTRAINT `student_answers_ibfk_2` FOREIGN KEY (`question_id`) REFERENCES `questions` (`id`) ON DELETE CASCADE,
CONSTRAINT `student_answers_ibfk_3` FOREIGN KEY (`selected_option_id`) REFERENCES `question_options` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
",
'exam_certificates' => "
CREATE TABLE IF NOT EXISTS `exam_certificates` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_exam_id` int(11) NOT NULL,
`certificate_number` varchar(50) NOT NULL,
`issue_date` date NOT NULL,
`expiry_date` date DEFAULT NULL,
`certificate_data` text,
`verification_code` varchar(50) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `certificate_number` (`certificate_number`),
UNIQUE KEY `verification_code` (`verification_code`),
KEY `student_exam_id` (`student_exam_id`),
CONSTRAINT `exam_certificates_ibfk_1` FOREIGN KEY (`student_exam_id`) REFERENCES `student_exams` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
"
];
// Add default settings
$default_settings = [
["default_passing_percentage", "40"],
["default_duration_minutes", "60"],
["randomize_questions", "1"],
["randomize_options", "1"],
["show_results_immediately", "1"],
["allow_exam_retake", "1"],
["max_retake_attempts", "3"],
["retake_waiting_period_days", "7"],
["block_exam_navigation", "0"],
["exam_result_certificate", "1"]
];
// Add a course FK relationship to question_banks if needed
$add_course_fk = "
ALTER TABLE `question_banks`
ADD CONSTRAINT `question_banks_ibfk_1`
FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE SET NULL;
";
// Add a course FK relationship to exam_schedules if needed
$add_course_fk_exams = "
ALTER TABLE `exam_schedules`
ADD CONSTRAINT `exam_schedules_ibfk_1`
FOREIGN KEY (`course_id`) REFERENCES `courses` (`id`) ON DELETE CASCADE;
";
// Add is_practice column to questions table if it doesn't exist
$add_is_practice_column = "
ALTER TABLE `questions`
ADD COLUMN `is_practice` tinyint(1) NOT NULL DEFAULT '0' AFTER `marks`
";
// Process each table creation
foreach ($tables as $table_name => $create_query) {
// Check if table exists
$table_exists_query = "SHOW TABLES LIKE '$table_name'";
$table_exists_result = $conn->query($table_exists_query);
if ($table_exists_result->num_rows > 0) {
$results[] = "Table <strong>$table_name</strong> already exists.";
} else {
// Create the table
try {
if ($conn->query($create_query)) {
$results[] = "Table <strong>$table_name</strong> created successfully.";
// If we just created the exam_settings table, populate default settings
if ($table_name === 'exam_settings') {
$insert_settings_query = "INSERT INTO `exam_settings` (`setting_key`, `setting_value`) VALUES (?, ?)";
$stmt = $conn->prepare($insert_settings_query);
foreach ($default_settings as $setting) {
$stmt->bind_param("ss", $setting[0], $setting[1]);
$stmt->execute();
}
$results[] = "Default exam settings added.";
}
} else {
$results[] = "Error creating table <strong>$table_name</strong>: " . $conn->error;
$success = false;
}
} catch (Exception $e) {
$results[] = "Exception creating table <strong>$table_name</strong>: " . $e->getMessage();
$success = false;
}
}
}
// Add foreign key constraints if needed
try {
// Check if the course_id column in question_banks has a foreign key
$fk_check_query = "SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'question_banks'
AND COLUMN_NAME = 'course_id'
AND REFERENCED_TABLE_NAME = 'courses'";
$fk_check_result = $conn->query($fk_check_query);
if ($fk_check_result->num_rows === 0) {
if ($conn->query($add_course_fk)) {
$results[] = "Foreign key constraint added for question_banks.course_id -> courses.id";
} else {
$results[] = "Error adding foreign key constraint to question_banks: " . $conn->error;
}
}
// Check if the course_id column in exam_schedules has a foreign key
$fk_check_exams_query = "SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = 'exam_schedules'
AND COLUMN_NAME = 'course_id'
AND REFERENCED_TABLE_NAME = 'courses'";
$fk_check_exams_result = $conn->query($fk_check_exams_query);
if ($fk_check_exams_result->num_rows === 0) {
if ($conn->query($add_course_fk_exams)) {
$results[] = "Foreign key constraint added for exam_schedules.course_id -> courses.id";
} else {
$results[] = "Error adding foreign key constraint to exam_schedules: " . $conn->error;
}
}
} catch (Exception $e) {
$results[] = "Exception adding foreign key constraints: " . $e->getMessage();
$success = false;
}
// Check and add is_practice column to questions table if it doesn't exist
try {
// Check if is_practice column exists in questions table
$column_check_query = "SHOW COLUMNS FROM `questions` LIKE 'is_practice'";
$column_check_result = $conn->query($column_check_query);
if ($column_check_result->num_rows === 0) {
// Column doesn't exist, add it
if ($conn->query($add_is_practice_column)) {
$results[] = "Added 'is_practice' column to questions table";
} else {
$results[] = "Error adding 'is_practice' column to questions table: " . $conn->error;
$success = false;
}
} else {
$results[] = "Column 'is_practice' already exists in questions table";
}
} catch (Exception $e) {
$results[] = "Exception checking/adding 'is_practice' column: " . $e->getMessage();
$success = false;
}
// Include header
include_once 'includes/header.php';
?>
<div class="container-fluid">
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Exam Database Setup</h1>
<a href="settings.php" class="btn btn-primary btn-sm shadow-sm">
<i class="fas fa-arrow-left fa-sm text-white-50"></i> Back to Exam Settings
</a>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Database Tables Status</h6>
</div>
<div class="card-body">
<?php if ($success): ?>
<div class="alert alert-success">
<i class="fas fa-check-circle"></i> Database setup completed successfully!
</div>
<?php else: ?>
<div class="alert alert-danger">
<i class="fas fa-exclamation-triangle"></i> There were issues with the database setup. Please check the details below.
</div>
<?php endif; ?>
<div class="table-responsive">
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>Operation</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<?php foreach ($results as $result): ?>
<tr>
<td><?php echo $result; ?></td>
<td>
<?php if (strpos($result, 'Error') !== false || strpos($result, 'Exception') !== false): ?>
<span class="badge bg-danger">Failed</span>
<?php else: ?>
<span class="badge bg-success">Success</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="text-center mt-4">
<a href="dashboard.php" class="btn btn-primary">
<i class="fas fa-tachometer-alt"></i> Go to Exam Dashboard
</a>
</div>
</div>
</div>
<!-- Schema Information -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Database Schema Information</h6>
</div>
<div class="card-body">
<p>The exam system uses the following tables:</p>
<div class="row">
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header">
<h6 class="font-weight-bold">Question Management</h6>
</div>
<div class="card-body">
<ul>
<li><strong>question_banks</strong> - Organizes questions by topic or course</li>
<li><strong>questions</strong> - Stores the question text, type, and difficulty</li>
<li><strong>question_options</strong> - Stores options for multiple choice questions</li>
</ul>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header">
<h6 class="font-weight-bold">Exam Management</h6>
</div>
<div class="card-body">
<ul>
<li><strong>exam_schedules</strong> - Defines exams, their timing, and settings</li>
<li><strong>exam_question_maps</strong> - Maps questions to specific exams</li>
<li><strong>exam_settings</strong> - Global settings for the exam system</li>
</ul>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header">
<h6 class="font-weight-bold">Student Attempts</h6>
</div>
<div class="card-body">
<ul>
<li><strong>student_exams</strong> - Tracks exam attempts by students</li>
<li><strong>student_answers</strong> - Records student answers for each question</li>
</ul>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card h-100">
<div class="card-header">
<h6 class="font-weight-bold">Certificates</h6>
</div>
<div class="card-body">
<ul>
<li><strong>exam_certificates</strong> - Stores certificates issued for passed exams</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php
// Include footer
include_once 'includes/footer.php';
?>