Jump to content

Olumide

Members
  • Posts

    126
  • Joined

  • Last visited

  • Days Won

    3

Everything posted by Olumide

  1. Thank you sir, but I have created a table for user_responses and yet, while using the pagination (next, previous) the selected option are all deselected. Attached is the screenshot of page1 and page 2 and database with my script DROP TABLE IF EXISTS `exams`; CREATE TABLE IF NOT EXISTS `exams` ( `id` int NOT NULL AUTO_INCREMENT, `title` varchar(255) DEFAULT NULL, `description` text, `duration` int DEFAULT NULL, `pass_mark` int NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=42 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; DROP TABLE IF EXISTS `questions`; CREATE TABLE IF NOT EXISTS `questions` ( `id` int NOT NULL AUTO_INCREMENT, `exam_id` int NOT NULL, `question` text NOT NULL, `answer_a` varchar(255) DEFAULT NULL, `answer_b` varchar(255) DEFAULT NULL, `answer_c` varchar(255) DEFAULT NULL, `answer_d` varchar(255) DEFAULT NULL, `correct_answer` char(1) DEFAULT NULL, PRIMARY KEY (`id`), KEY `exam_id` (`exam_id`) ) ENGINE=MyISAM AUTO_INCREMENT=252 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; DROP TABLE IF EXISTS `results`; CREATE TABLE IF NOT EXISTS `results` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int DEFAULT NULL, `exam_id` int DEFAULT NULL, `score` int DEFAULT NULL, PRIMARY KEY (`id`), KEY `user_id` (`user_id`), KEY `exam_id` (`exam_id`) ) ENGINE=MyISAM AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; DROP TABLE IF EXISTS `users`; CREATE TABLE IF NOT EXISTS `users` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL, `email` varchar(100) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `role` enum('admin','student') DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; DROP TABLE IF EXISTS `user_responses`; CREATE TABLE IF NOT EXISTS `user_responses` ( `id` int NOT NULL AUTO_INCREMENT, `user_id` int NOT NULL, `exam_id` int NOT NULL, `question_id` int NOT NULL, `selected_option` varchar(255) NOT NULL, `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; exam.php <?php if (session_status() == PHP_SESSION_NONE) { session_start(); } include 'db_connect.php'; if (!isset($_SESSION['fullName']) || !isset($_SESSION['email']) || !isset($_SESSION['exam_id']) || !isset($_SESSION['user_id'])) { header('Location: instruction.php?exam_id=' . $_SESSION['exam_id']); exit; } $exam_id = $_SESSION['exam_id']; $user_id = $_SESSION['user_id']; $fullName = $_SESSION['fullName']; $email = $_SESSION['email']; $stmt = $pdo->prepare('SELECT * FROM exams WHERE id = ?'); $stmt->execute([$exam_id]); $exam = $stmt->fetch(PDO::FETCH_ASSOC); if (!$exam) { die('Exam not found'); } $stmt = $pdo->prepare('SELECT * FROM questions WHERE exam_id = ?'); $stmt->execute([$exam_id]); $questions = $stmt->fetchAll(PDO::FETCH_ASSOC); $remainingTime = $_SESSION['remaining_time'] ?? ($exam['duration'] * 60); if (isset($_SESSION['exam_start_time'])) { $elapsedTime = time() - $_SESSION['exam_start_time']; $remainingTime = max(0, $remainingTime - $elapsedTime); } else { $_SESSION['exam_start_time'] = time(); } $_SESSION['remaining_time'] = $remainingTime; $selected_answers = $_SESSION['selected_answers'] ?? []; $questionsPerPage = 5; $totalQuestions = count($questions); $totalPages = ceil($totalQuestions / $questionsPerPage); $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; $page = max(1, min($page, $totalPages)); $offset = ($page - 1) * $questionsPerPage; $questionsToDisplay = array_slice($questions, $offset, $questionsPerPage); ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php echo htmlspecialchars($exam['title']); ?></title> <link rel="stylesheet" type="text/css" href="styles.css"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> $(document).ready(function() { let duration = <?php echo $remainingTime; ?>; let timer; function startTimer() { timer = setInterval(function() { duration--; if (duration < 0) duration = 0; $('#timer').text(`${Math.floor(duration / 60)}:${duration % 60 < 10 ? '0' : ''}${duration % 60}`); if (duration === 0) { clearInterval(timer); submitExam(); } else if (duration <= 30) { $('#examForm input[type="radio"]').prop('disabled', true); if (duration === 5) { new Audio('alarm.mp3').play(); } } }, 1000); } startTimer(); function saveAnswers(callback) { let answers = {}; $('#examForm input[type="radio"]:checked').each(function() { let questionId = $(this).attr('name').match(/\d+/)[0]; let value = $(this).val(); answers[questionId] = value; }); $.ajax({ url: 'save_answers.php', type: 'POST', data: { answers: JSON.stringify(answers) }, success: function(response) { if (callback) callback(); } }); } function submitExam() { clearInterval(timer); let answers = {}; $('#examForm input[type="radio"]:checked').each(function() { let questionId = $(this).attr('name').match(/\d+/)[0]; let value = $(this).val(); answers[questionId] = value; }); $('<input>').attr({ type: 'hidden', name: 'selected_answers', value: JSON.stringify(answers) }).appendTo('#examForm'); $.ajax({ url: 'submit_exam.php', type: 'POST', data: $('#examForm').serialize(), success: function(response) { $('#examStatus').html(response); $('#examForm').hide(); }, error: function() { $('#examStatus').html('Error submitting exam.'); } }); } $('#examForm').on('submit', function(e) { e.preventDefault(); if (duration > 0) { if (confirm('Are you sure you want to submit the exam?')) { saveAnswers(submitExam); } else { if (timer) clearInterval(timer); startTimer(); } } else { submitExam(); } }); $('.pagination a').on('click', function(e) { e.preventDefault(); saveAnswers(function() { window.location.href = $(this).attr('href'); }.bind(this)); }); function preselectAnswers() { let selectedAnswers = <?php echo json_encode($selected_answers); ?>; for (let questionId in selectedAnswers) { let answer = selectedAnswers[questionId]; $(`input[name="answers[${questionId}]"][value="${answer}"]`).prop('checked', true); } } preselectAnswers(); }); </script> </head> <body> <div class="header"> <div class="timer">Time Remaining: <span id="timer"></span></div> <div class="subject"> <h1><?php echo htmlspecialchars($exam['title']); ?></h1> </div> <div class="name"> <?php echo htmlspecialchars($fullName); ?> </div> </div> <div style="padding-top: 60px; height: calc(100vh - 60px); overflow: auto;"> <form id="examForm"> <input type="hidden" name="exam_id" value="<?php echo htmlspecialchars($exam['id']); ?>"> <?php foreach ($questionsToDisplay as $index => $question): ?> <div class="question"> <p><?php echo ($offset + $index + 1) . '. ' . htmlspecialchars($question['question']); ?></p> <?php $options = [ 'A' => $question['answer_a'] ?? '', 'B' => $question['answer_b'] ?? '', 'C' => $question['answer_c'] ?? '', 'D' => $question['answer_d'] ?? '', ]; ?> <ul style="list-style-type: none;"> <?php foreach ($options as $key => $option): ?> <?php if (!empty($option)): ?> <li> <label> <input type="radio" name="answers[<?php echo $question['id']; ?>]" value="<?php echo htmlspecialchars($key); ?>" <?php echo (isset($selected_answers[$question['id']]) && $selected_answers[$question['id']] === $key) ? 'checked' : ''; ?>> <?php echo htmlspecialchars($key . '. ' . $option); ?> </label> </li> <?php endif; ?> <?php endforeach; ?> </ul> </div> <?php endforeach; ?> <div class="pagination"> <?php if ($page > 1): ?> <a href="?page=<?php echo $page - 1; ?>">&laquo; Previous</a> <?php endif; ?> <?php if ($page < $totalPages): ?> <a href="?page=<?php echo $page + 1; ?>">Next &raquo;</a> <?php endif; ?> </div> <div style="text-align: center;"> <button type="submit" style="display: inline-block;">Submit</button> </div> </form> <div id="examStatus"></div> </div> </body> </html> save_answers.php <?php if (session_status() == PHP_SESSION_NONE) { session_start(); } include 'db_connect.php'; if (!isset($_SESSION['exam_id']) || !isset($_SESSION['user_id'])) { die('Invalid request'); } $exam_id = $_SESSION['exam_id']; $user_id = $_SESSION['user_id']; $answers = json_decode($_POST['answers'], true); if (!$answers) { die('No answers to save'); } $_SESSION['selected_answers'] = array_merge($_SESSION['selected_answers'] ?? [], $answers); try { foreach ($answers as $question_id => $selected_option) { $stmt = $pdo->prepare('REPLACE INTO user_responses (user_id, exam_id, question_id, selected_option) VALUES (?, ?, ?, ?)'); $stmt->execute([$user_id, $exam_id, $question_id, $selected_option]); } echo 'Answers saved successfully'; } catch (PDOException $e) { echo 'Error saving answers: ' . $e->getMessage(); } submit_exam.php <?php if (session_status() == PHP_SESSION_NONE) { session_start(); } include 'db_connect.php'; // Ensure this file sets up the $pdo variable if ($_SERVER['REQUEST_METHOD'] === 'POST') { $exam_id = $_SESSION['exam_id']; $user_id = $_SESSION['user_id']; $selected_answers = json_decode($_POST['selected_answers'], true); // Insert or update user responses foreach ($selected_answers as $question_id => $selected_option) { $stmt = $pdo->prepare('REPLACE INTO user_responses (user_id, exam_id, question_id, selected_option) VALUES (?, ?, ?, ?)'); $stmt->execute([$user_id, $exam_id, $question_id, $selected_option]); } // Calculate the score $score = 0; foreach ($selected_answers as $question_id => $selected_option) { $stmt = $pdo->prepare('SELECT correct_answer FROM questions WHERE id = ?'); $stmt->execute([$question_id]); $correct_answer = $stmt->fetchColumn(); if ($correct_answer === $selected_option) { $score++; } } // Insert or update the result in the results table $stmt = $pdo->prepare('REPLACE INTO results (user_id, exam_id, score) VALUES (?, ?, ?)'); $stmt->execute([$user_id, $exam_id, $score]); // Destroy the session session_unset(); session_destroy(); // Response message echo "Exam submitted successfully! Your score is: $score"; } ?> Screen shot
  2. Hi, I am having issues with this cbt project whereby my questions are selected 5 per page and when the user click on NEXT, the first page selected radio button option should be saved so that when the user click on previous button, he should be able to see the selected options. But I have been finding difficult to do. And I don't think it is right to create another table again to store the user answers because in each subjects, there are more than 15 questions. Please here is exam script and the screen short of the first page of the exam. <!DOCTYPE html> <html> <head> <title><?php echo htmlspecialchars($exam['title']); ?></title> <link rel="stylesheet" type="text/css" href="styles.css"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> $(document).ready(function() { let duration = <?php echo $remainingTime; ?>; let timer; function startTimer() { timer = setInterval(function() { duration--; if (duration < 0) duration = 0; $('#timer').text(`${Math.floor(duration / 60)}:${duration % 60 < 10 ? '0' : ''}${duration % 60}`); if (duration === 0) { clearInterval(timer); submitExam(); // Automatically submit the form when time expires } else if (duration <= 30) { // Disable form elements when less than 30 seconds remain $('#examForm input[type="radio"]').prop('disabled', true); if (duration === 5) { // Play alarm sound if time is below 5 seconds new Audio('alarm.mp3').play(); } } }, 1000); } startTimer(); function saveAnswers() { let answers = {}; $('#examForm input[type="radio"]:checked').each(function() { let name = $(this).attr('name'); let value = $(this).val(); answers[name] = value; }); return $.ajax({ url: 'save_answers.php', type: 'POST', data: { page: <?php echo $page; ?>, answers: JSON.stringify(answers) } }); } function submitExam() { clearInterval(timer); let answers = {}; $('#examForm input[type="radio"]:checked').each(function() { let name = $(this).attr('name'); let value = $(this).val(); answers[name] = value; }); $('<input>').attr({ type: 'hidden', name: 'selected_answers', value: JSON.stringify(answers) }).appendTo('#examForm'); $.ajax({ url: 'submit_exam.php', type: 'POST', data: $('#examForm').serialize(), success: function(response) { $('#examStatus').html(response); $('#examForm').hide(); // Hide the form after submission }, error: function() { $('#examStatus').html('Error submitting exam.'); } }); } $('#examForm').on('submit', function(e) { e.preventDefault(); if (duration > 0) { if (confirm('Are you sure you want to submit the exam?')) { saveAnswers().done(function() { submitExam(); }); } else { if (timer) clearInterval(timer); startTimer(); } } else { submitExam(); } }); $('.pagination a').on('click', function(e) { e.preventDefault(); saveAnswers().done(function() { window.location.href = $(this).attr('href'); // Navigate to the new page }.bind(this)); }); function preselectAnswers() { let selectedAnswers = <?php echo json_encode($selected_answers); ?>; for (let questionId in selectedAnswers) { let answer = selectedAnswers[questionId]; $(`input[name="answers[${questionId}]"][value="${answer}"]`).prop('checked', true); } } preselectAnswers(); }); </script> </head> <body> <div class="header"> <div class="timer">Time Remaining: <span id="timer"></span></div> <div class="subject"> <h1><?php echo htmlspecialchars($exam['title']); ?></h1> </div> <div class="name"> <?php echo htmlspecialchars($fullName); ?> </div> </div> <div style="padding-top: 60px; height: calc(100vh - 60px); overflow: auto;"> <!-- Adjusted for fixed header --> <form id="examForm"> <input type="hidden" name="exam_id" value="<?php echo htmlspecialchars($exam['id']); ?>"> <?php foreach ($questionsToDisplay as $index => $question): ?> <div class="question"> <p><?php echo ($offset + $index + 1) . '. ' . htmlspecialchars($question['question']); ?></p> <?php $options = isset($question['options']) ? json_decode($question['options'], true) : null; if (is_null($options) || !is_array($options)) { $options = [ 'A' => $question['answer_a'] ?? '', 'B' => $question['answer_b'] ?? '', 'C' => $question['answer_c'] ?? '', 'D' => $question['answer_d'] ?? '', ]; } ?> <ul style="list-style-type: none;"> <?php foreach ($options as $key => $option): ?> <?php if (!empty($option)): ?> <li> <label> <input type="radio" name="answers[<?php echo $question['id']; ?>]" value="<?php echo htmlspecialchars($key); ?>" <?php echo (isset($_SESSION['selected_answers'][$question['id']]) && $_SESSION['selected_answers'][$question['id']] === $key) ? 'checked' : ''; ?>> <?php echo htmlspecialchars($key . '. ' . $option); ?> </label> </li> <?php endif; ?> <?php endforeach; ?> </ul> </div> <?php endforeach; ?> <div class="pagination"> <?php if ($page > 1): ?> <a href="?page=<?php echo ($page - 1); ?>" class="pagination button">&laquo; Previous</a> <?php endif; ?> <?php if ($page < $totalPages): ?> <a href="?page=<?php echo ($page + 1); ?>" class="pagination button">Next &raquo;</a> <?php endif; ?> </div> <button type="submit">Submit Exam</button> </form> <div id="examStatus"></div> </div> </body> </html> save_answers.php <?php session_start(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { $page = isset($_POST['page']) ? (int)$_POST['page'] : 0; $answers = isset($_POST['answers']) ? json_decode($_POST['answers'], true) : []; if (!isset($_SESSION['selected_answers'])) { $_SESSION['selected_answers'] = []; } // Update session with the received answers $_SESSION['selected_answers'][$page] = $answers; echo 'Answers saved.'; } ?> screen short
  3. Hi, I am currently using a shared hosting provider that includes cPanel, MySQL database support, and phpMyAdmin, but I've been experiencing frequent fluctuations and downtime. I'm looking for a more stable and reliable hosting solution that is also cost-effective. Here are my requirements: cPanel or Equivalent: I need a control panel for easy website and server management. MySQL Database Support: Reliable MySQL database hosting is essential. phpMyAdmin: A web-based interface for managing MySQL databases. Uptime and Stability: A hosting provider known for good uptime and stability. Cost-Effective: Affordable pricing, ideally comparable to shared hosting costs. Additional Features: Any additional features like automatic backups, SSL certificates, and support for PHP applications would be a plus. Could you please recommend any hosting providers that meet these criteria? Personal experiences and specific plans that you have had success with would be greatly appreciated. Thank you for your assistance!
  4. You'll need to install Composer on your web server. Composer is a dependency manager for PHP that allows you to easily manage libraries and packages. Go to the https://getcomposer.org/, download the Installer for Composer depending on your OS. Open the downloaded Composer-Setup.exe (Windows OS as an example) file. Follow the prompts in the installation wizard. Make sure to select the option to add Composer to the system PATH during installation. Verify Installation: Open Command Prompt. Type composer and press Enter. You should see the Composer version and available commands, confirming that Composer is installed and accessible globally. Open Command Prompt and navigate to your project directory: cd path\to\your\project\directory Use Composer to install the PhpSpreadsheet library by creating a composer.json file and running composer install. Include the Composer autoload file in your PHP script and use PhpSpreadsheet to read your Excel files.
  5. To read .xlsx files using PHP 8, you can use the PhpSpreadsheet library. It's the successor to PHPExcel and fully supports PHP 8. You can run this command "composer require phpoffice/phpspreadsheet" and Once you have PhpSpreadsheet installed, you can use it to read .xlsx files. Here's a basic example: <?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\IOFactory; try { $spreadsheet = IOFactory::load('path/to/your/file.xlsx'); $sheet = $spreadsheet->getActiveSheet(); // Get the highest row and column numbers referenced in the worksheet $highestRow = $sheet->getHighestRow(); // e.g. 10 $highestColumn = $sheet->getHighestColumn(); // e.g 'F' // Iterate over each row in the worksheet in turn for ($row = 1; $row <= $highestRow; $row++) { $rowData = $sheet->rangeToArray('A' . $row . ':' . $highestColumn . $row, NULL, TRUE, FALSE); print_r($rowData); } } catch (Exception $e) { echo 'Error loading file: ', $e->getMessage(); } ?>
  6. Thank you so much for the support and all the guidance. Though, I have solved the problem by using instascan instead of the previous quagga and html5 qrcode. But getting worked is nothing but to take to practices all the programming advices you have highlighted which is very essential. I cannot but to always thank you all in this forum for your help and I can say ever since I have joined this forum, I have never for once regret that. You are all amazing. Cheers
  7. I want to design an attendance system such that the class teacher can scan students' ID card QR code using their phone, and it will be recorded immediately in the database. Currently, I have tested HTML QR code and Quagga, but I haven't tested scanning the ID card yet. I am only testing with a phone and PC camera because I want the user to be able to use either device to scan the student ID card. I'm not sure where the error is. Could anyone please enlighten me? Here is the dashboard.php where the user can click scan ID Card <?php // pages/dashboard.php session_start(); if (!isset($_SESSION['username'])) { header('Location: login.php'); exit(); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="../css/styles.css"> <title>Dashboard</title> </head> <body> <div class="container"> <header> <div id="branding"> <h1>QR Smart Attendance</h1> </div> <nav> <ul> <li><a href="dashboard.php">Dashboard</a></li> <li><a href="generate_id_card.php">Generate ID Card</a></li> <li><a href="record_attendance.php">Record Attendance</a></li> <li><a href="transfer_attendance.php">Transfer Attendance</a></li> <li><a href="logout.php">Logout</a></li> </ul> </nav> </header> <div class="dashboard-container"> <h2>Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?></h2> <!-- Add a button for scanning ID cards --> <button id="scanButton">Scan ID Card</button> <!-- Container for the scanner --> <div id="scanner-container" style="width: 100%; height: 400px; display:none;"></div> <script src="https://unpkg.com/@ericblade/quagga2/dist/quagga.min.js"></script> <script> document.getElementById('scanButton').addEventListener('click', function() { // Display the scanner container document.getElementById('scanner-container').style.display = 'block'; Quagga.init({ inputStream: { name: "Live", type: "LiveStream", target: document.querySelector('#scanner-container'), constraints: { width: 640, height: 480, facingMode: "environment" // or "user" for front-facing camera } }, decoder: { readers: ["qr_reader"] // Use the QR reader }, locate: true // Enable locating the QR code in the image }, function(err) { if (err) { console.error('QuaggaJS initialization failed:', err); return; } console.log("QuaggaJS initialization finished. Ready to start"); Quagga.start(); }); // Handle scanned results Quagga.onDetected(function(result) { if (result && result.codeResult && result.codeResult.code) { console.log('Scanned code:', result.codeResult.code); // Redirect to record_attendance.php with student_id or handle accordingly window.location.href = 'record_attendance.php?student_id=' + result.codeResult.code; } else { console.warn('No code detected'); } }); // Optional: Handle processing errors Quagga.onProcessed(function(result) { var drawingCtx = Quagga.canvas.ctx.overlay, drawingCanvas = Quagga.canvas.dom.overlay; if (result) { if (result.boxes) { drawingCtx.clearRect(0, 0, drawingCanvas.width, drawingCanvas.height); result.boxes.filter(function(box) { return box !== result.box; }).forEach(function(box) { Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 2 }); }); } if (result.box) { Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "#00F", lineWidth: 2 }); } if (result.codeResult && result.codeResult.code) { Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: 'red', lineWidth: 3 }); } } }); }); </script> </div> </div> </body> </html> And here is record_attendance.php <?php // pages/record_attendance.php session_start(); if (!isset($_SESSION['username'])) { header('Location: login.php'); exit(); } require_once '../includes/db.php'; // Include db.php for database connection and functions // Check if a student ID is scanned and process attendance if (isset($_GET['student_id'])) { $student_id = sanitizeInput($_GET['student_id']); $date = date('Y-m-d'); // Check if attendance record already exists for today $query = "SELECT * FROM attendance WHERE student_id = ? AND date = ?"; $stmt = $conn->prepare($query); $stmt->bind_param("is", $student_id, $date); $stmt->execute(); $stmt->store_result(); if ($stmt->num_rows == 0) { // If no record exists, insert new attendance record $query = "INSERT INTO attendance (student_id, date, status) VALUES (?, ?, 'present')"; $stmt = $conn->prepare($query); $stmt->bind_param("is", $student_id, $date); $stmt->execute(); // Optional: Redirect back to dashboard after recording attendance header('Location: dashboard.php'); exit(); } else { // Optional: Handle case where attendance is already recorded for today echo "Attendance already recorded for today."; // Redirect or display message as needed } } else { // Handle case where no student ID is scanned echo "No student ID received. Please scan a valid student ID."; // Redirect or display message as needed } ?> Here is generate_id_card.php <?php // pages/generate_id_card.php session_start(); if (!isset($_SESSION['username']) || $_SESSION['role'] != 'admin') { header('Location: login.php'); exit(); } require_once '../includes/db.php'; // Ensure this path is correct require_once '../includes/functions.php'; if ($_SERVER['REQUEST_METHOD'] == 'POST') { $name = sanitizeInput($_POST['name']); $class = sanitizeInput($_POST['class']); $photo = $_FILES['photo']; if ($photo['error'] == 0) { $photo_path = '../images/' . basename($photo['name']); move_uploaded_file($photo['tmp_name'], $photo_path); // Insert student data into the database $insert_query = "INSERT INTO students (name, class, photo) VALUES (?, ?, ?)"; $stmt = $conn->prepare($insert_query); $stmt->bind_param("sss", $name, $class, $photo_path); $stmt->execute(); // Retrieve the inserted student's ID $student_id = $conn->insert_id; // Generate ID card with the student's details $id_card = generateIDCard($name, $class, $photo_path); echo "<p>ID Card generated: <a href='$id_card'>Download ID Card</a></p>"; } else { echo "<p>Error uploading photo.</p>"; } } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="../css/styles.css"> <title>Generate ID Card</title> </head> <body> <div class="container"> <header> <div id="branding"> <h1>QR Smart Attendance</h1> </div> </header> <div class="generate-container"> <h2>Generate Student ID Card</h2> <form method="post" enctype="multipart/form-data"> <input type="text" name="name" placeholder="Student Name" required> <input type="text" name="class" placeholder="Class" required> <input type="file" name="photo" accept="image/*" required> <button type="submit">Generate</button> </form> </div> </div> </body> </html> Here is functions.php <?php require_once '../vendor/autoload.php'; use Endroid\QrCode\QrCode; use Endroid\QrCode\Writer\PngWriter; function generateIDCard($name, $class, $photo_path) { // Ensure QR code directory exists $qrCodeDir = '../images/qrcodes'; if (!is_dir($qrCodeDir)) { mkdir($qrCodeDir, 0755, true); } // Generate a unique ID for the QR code $uniqueId = uniqid(); // Create the QR code $qrCode = new QrCode($uniqueId); $qrCode->setSize(300); // Set the QR code file path $qrCodePath = "$qrCodeDir/$uniqueId.png"; $qrCode->writeFile($qrCodePath); // Ensure font file exists $fontPath = '../fonts/arial.ttf'; if (!file_exists($fontPath)) { die('Font file not found.'); } // Create the ID card image $idCard = imagecreate(600, 400); $white = imagecolorallocate($idCard, 255, 255, 255); $black = imagecolorallocate($idCard, 0, 0, 0); // Add student details imagettftext($idCard, 20, 0, 20, 50, $black, $fontPath, "Name: $name"); imagettftext($idCard, 20, 0, 20, 100, $black, $fontPath, "Class: $class"); imagettftext($idCard, 20, 0, 20, 150, $black, $fontPath, "ID: $uniqueId"); // Add the student photo $photo = imagecreatefromjpeg($photo_path); if ($photo) { imagecopyresized($idCard, $photo, 400, 20, 0, 0, 160, 160, imagesx($photo), imagesy($photo)); } else { die('Photo file not found.'); } // Add the QR code $qrImage = imagecreatefrompng($qrCodePath); if ($qrImage) { imagecopyresized($idCard, $qrImage, 20, 200, 0, 0, 160, 160, imagesx($qrImage), imagesy($qrImage)); } else { die('QR code file not found.'); } // Ensure ID card directory exists $idCardDir = '../images/idcards'; if (!is_dir($idCardDir)) { mkdir($idCardDir, 0755, true); } // Save the ID card image $idCardPath = "$idCardDir/$uniqueId.png"; imagepng($idCard, $idCardPath); // Free up memory imagedestroy($idCard); imagedestroy($photo); imagedestroy($qrImage); return $idCardPath; } ?> My database DROP TABLE IF EXISTS `attendance`; CREATE TABLE IF NOT EXISTS `attendance` ( `id` int NOT NULL AUTO_INCREMENT, `student_id` int NOT NULL, `date` date NOT NULL, `status` enum('present','absent') NOT NULL, PRIMARY KEY (`id`), KEY `student_id` (`student_id`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; DROP TABLE IF EXISTS `notifications`; CREATE TABLE IF NOT EXISTS `notifications` ( `id` int NOT NULL AUTO_INCREMENT, `message` text NOT NULL, `date` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; -- -------------------------------------------------------- -- -- Table structure for table `students` -- DROP TABLE IF EXISTS `students`; CREATE TABLE IF NOT EXISTS `students` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `class` varchar(50) NOT NULL, `photo` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; DROP TABLE IF EXISTS `users`; CREATE TABLE IF NOT EXISTS `users` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(50) NOT NULL, `password` varchar(255) NOT NULL, `role` enum('admin','teacher') NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `username` (`username`) ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
  8. Olumide

    CBT Exam

    I appreciate you, but that does not give me the options. Though, I pending the project for now as it is taking more time and still no solution. I will still get back on it.
  9. Olumide

    CBT Exam

    I keep getting this error Warning: DOMDocument::loadXML(): Namespace prefix m on oMath is not defined in Entity, line: 1 in C:\wamp64\www\cbt\cbt_exam\vendor\phpoffice\math\src\Math\Reader\OfficeMathML.php on line 39 Call Stack #TimeMemoryFunctionLocation 10.0005449024{main}( )...\upload.php:0 20.0080581080convertDocxToHtml( $filename = 'uploads/Question_me2.docx' )...\upload.php:30 30.0087581080PhpOffice\PhpWord\IOFactory::load( $filename = 'uploads/Question_me2.docx', $readerName = ??? )...\upload.php:120 40.0100581872PhpOffice\PhpWord\Reader\Word2007->load( $docFile = 'uploads/Question_me2.docx' )...\IOFactory.php:89 50.0479598432PhpOffice\PhpWord\Reader\Word2007->readPart( $phpWord = class PhpOffice\PhpWord\PhpWord { private $sections = [0 => class PhpOffice\PhpWord\Element\Section { ... }]; private $collections = ['Bookmarks' => class PhpOffice\PhpWord\Collection\Bookmarks { ... }, 'Titles' => class PhpOffice\PhpWord\Collection\Titles { ... }, 'Footnotes' => class PhpOffice\PhpWord\Collection\Footnotes { ... }, 'Endnotes' => class PhpOffice\PhpWord\Collection\Endnotes { ... }, 'Charts' => class PhpOffice\PhpWord\Collection\Charts { ... }, 'Comments' => class PhpOffice\PhpWord\Collection\Comments { ... }]; private $metadata = ['DocInfo' => class PhpOffice\PhpWord\Metadata\DocInfo { ... }, 'Settings' => class PhpOffice\PhpWord\Metadata\Settings { ... }, 'Compatibility' => class PhpOffice\PhpWord\Metadata\Compatibility { ... }] }, $relationships = ['main' => ['rId1' => [...], 'rId2' => [...], 'rId3' => [...]], 'document' => ['rId1' => [...], 'rId2' => [...], 'rId3' => [...], 'rId4' => [...], 'rId5' => [...], 'rId6' => [...]]], $commentRefs = [], $partName = 'Document', $docFile = 'uploads/Question_me2.docx', $xmlFile = 'word/document.xml' )...\Word2007.php:89 and here is the OfficeMathML.php <?php namespace PhpOffice\Math\Reader; use DOMDocument; use DOMNode; use DOMXPath; use PhpOffice\Math\Element; use PhpOffice\Math\Exception\InvalidInputException; use PhpOffice\Math\Exception\NotImplementedException; use PhpOffice\Math\Math; class OfficeMathML implements ReaderInterface { /** @var DOMDocument */ protected $dom; /** @var Math */ protected $math; /** @var DOMXpath */ protected $xpath; /** @var string[] */ protected $operators = ['+', '-', '/', '∗']; public function read(string $content): ?Math { $nsMath = 'xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"'; $nsWord = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"'; $content = str_replace( $nsMath, $nsMath . ' ' . $nsWord, $content ); $this->dom = new DOMDocument(); $this->dom->loadXML($content); $this->math = new Math(); $this->parseNode(null, $this->math); return $this->math; } /** * @see https://devblogs.microsoft.com/math-in-office/officemath/ * @see https://learn.microsoft.com/fr-fr/archive/blogs/murrays/mathml-and-ecma-math-omml * * @param Math|Element\AbstractGroupElement $parent */ protected function parseNode(?DOMNode $nodeRowElement, $parent): void { $this->xpath = new DOMXpath($this->dom); foreach ($this->xpath->query('*', $nodeRowElement) ?: [] as $nodeElement) { $element = $this->getElement($nodeElement); $parent->add($element); if ($element instanceof Element\AbstractGroupElement) { $this->parseNode($nodeElement, $element); } } } protected function getElement(DOMNode $nodeElement): Element\AbstractElement { switch ($nodeElement->nodeName) { case 'm:f': // Numerator $nodeNumerator = $this->xpath->query('m:num/m:r/m:t', $nodeElement); if ($nodeNumerator && $nodeNumerator->length == 1) { $value = $nodeNumerator->item(0)->nodeValue; if (is_numeric($value)) { $numerator = new Element\Numeric(floatval($value)); } else { $numerator = new Element\Identifier($value); } } else { throw new InvalidInputException(sprintf( '%s : The tag `%s` has no numerator defined', __METHOD__, $nodeElement->nodeName )); } // Denominator $nodeDenominator = $this->xpath->query('m:den/m:r/m:t', $nodeElement); if ($nodeDenominator && $nodeDenominator->length == 1) { $value = $nodeDenominator->item(0)->nodeValue; if (is_numeric($value)) { $denominator = new Element\Numeric(floatval($value)); } else { $denominator = new Element\Identifier($value); } } else { throw new InvalidInputException(sprintf( '%s : The tag `%s` has no denominator defined', __METHOD__, $nodeElement->nodeName )); } return new Element\Fraction($numerator, $denominator); case 'm:r': $nodeText = $this->xpath->query('m:t', $nodeElement); if ($nodeText && $nodeText->length == 1) { $value = trim($nodeText->item(0)->nodeValue); if (in_array($value, $this->operators)) { return new Element\Operator($value); } if (is_numeric($value)) { return new Element\Numeric(floatval($value)); } return new Element\Identifier($value); } throw new InvalidInputException(sprintf( '%s : The tag `%s` has no tag `m:t` defined', __METHOD__, $nodeElement->nodeName )); case 'm:oMath': return new Element\Row(); default: throw new NotImplementedException(sprintf( '%s : The tag `%s` is not implemented', __METHOD__, $nodeElement->nodeName )); } } } And here is my upload.php <?php session_start(); if ($_SESSION['role'] !== 'teacher') { header("Location: login.php"); exit(); } require 'db.php'; require 'vendor/autoload.php'; // Load the PHPWord library use PhpOffice\PhpWord\IOFactory; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Shared\Html; $database = new Database(); $db = $database->getConnection(); if ($_SERVER['REQUEST_METHOD'] == 'POST') { $class_id = $_POST['class_id']; $subject_id = $_POST['subject_id']; $file = $_FILES['file']; if ($file['type'] == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { $upload_dir = 'uploads/'; $file_path = $upload_dir . basename($file['name']); if (move_uploaded_file($file['tmp_name'], $file_path)) { try { // Convert .docx file to HTML $htmlFilePath = convertDocxToHtml($file_path); if ($htmlFilePath) { echo "File converted to HTML successfully.<br>"; } else { echo "Failed to convert file to HTML.<br>"; exit(); } // Parse the HTML file $htmlContent = file_get_contents($htmlFilePath); $questions = parseHtmlToQuestions($htmlContent); foreach ($questions as $question) { $stmt = $db->prepare("INSERT INTO questions (question_text, question_type, option_a, option_b, option_c, option_d, correct_option, subject_id) VALUES (?, 'Multiple Choice', ?, ?, ?, ?, ?, ?)"); if ($stmt->execute([ $question['question_text'], $question['options']['a'] ?? '', $question['options']['b'] ?? '', $question['options']['c'] ?? '', $question['options']['d'] ?? '', $question['correct_option'], $subject_id ])) { echo "Question inserted successfully: " . htmlspecialchars($question['question_text']) . "<br>"; } else { echo "Failed to insert question: " . htmlspecialchars($question['question_text']) . "<br>"; print_r($stmt->errorInfo()); } } echo "File uploaded and questions added successfully."; } catch (Exception $e) { echo "Error processing file: " . htmlspecialchars($e->getMessage()); } } else { echo "Failed to upload file."; } } else { echo "Invalid file type. Please upload a .docx file."; } } else { $classes = $db->query("SELECT * FROM classes")->fetchAll(PDO::FETCH_ASSOC); ?> <!DOCTYPE html> <html> <head> <title>Upload Questions</title> </head> <body> <h1>Upload Questions</h1> <form action="upload.php" method="post" enctype="multipart/form-data"> <label for="class">Class:</label> <select name="class_id" id="class" onchange="fetchSubjects(this.value)"> <option value="">Select Class</option> <?php foreach ($classes as $class): ?> <option value="<?= htmlspecialchars($class['class_id']) ?>"><?= htmlspecialchars($class['class_name']) ?></option> <?php endforeach; ?> </select> <label for="subject">Subject:</label> <select name="subject_id" id="subject"> <option value="">Select Subject</option> <!-- Subjects will be populated based on selected class --> </select> <label for="file">Choose a file:</label> <input type="file" name="file" id="file" accept=".docx"> <button type="submit">Upload</button> </form> <script> function fetchSubjects(classId) { fetch(`fetch_subjects.php?class_id=${classId}`) .then(response => response.json()) .then(data => { const subjectSelect = document.getElementById('subject'); subjectSelect.innerHTML = '<option value="">Select Subject</option>'; data.forEach(subject => { const option = document.createElement('option'); option.value = subject.subject_id; option.textContent = subject.subject_name; subjectSelect.appendChild(option); }); }); } </script> </body> </html> <?php } function convertDocxToHtml($filename) { $phpWord = IOFactory::load($filename); $htmlWriter = IOFactory::createWriter($phpWord, 'HTML'); $htmlFilePath = str_replace('.docx', '.html', $filename); $htmlWriter->save($htmlFilePath); return $htmlFilePath; } function parseHtmlToQuestions($htmlContent) { $questions = []; $currentQuestion = null; $dom = new DOMDocument; @$dom->loadHTML($htmlContent); $paragraphs = $dom->getElementsByTagName('p'); $currentPassage = ''; foreach ($paragraphs as $paragraph) { $text = trim($paragraph->textContent); $nodeContent = ''; foreach ($paragraph->childNodes as $node) { if ($node->nodeName === 'b' || $node->nodeName === 'strong') { $nodeContent .= '<b>' . $node->textContent . '</b>'; } elseif ($node->nodeName === 'i' || $node->nodeName === 'em') { $nodeContent .= '<i>' . $node->textContent . '</i>'; } elseif ($node->nodeName === 'img') { $src = $node->getAttribute('src'); $nodeContent .= '<img src="' . $src . '">'; } else { $nodeContent .= $node->textContent; } } // Handle equations (assuming they are marked with a specific tag or format) if (strpos($text, 'Equation:') === 0) { $equation = substr($text, 9); $currentQuestion['equation'] = $equation; // Store equations separately } if (strpos($text, 'Passage:') === 0) { if ($currentPassage) { $questions[] = ['passage' => $currentPassage, 'questions' => []]; } $currentPassage = substr($text, 8); } elseif (strpos($text, 'Question:') === 0) { if ($currentQuestion) { $questions[] = $currentQuestion; } $currentQuestion = [ 'question_text' => $nodeContent, 'options' => [], 'correct_option' => '', 'equation' => '', 'images' => [] ]; } elseif (preg_match('/^[A-D]\)/', $text)) { if ($currentQuestion) { $optionKey = strtolower($text[0]); $currentQuestion['options'][$optionKey] = substr($text, 3); } } elseif (strpos($text, 'Answer:') === 0) { if ($currentQuestion) { $currentQuestion['correct_option'] = trim(substr($text, 7)); } } } if ($currentQuestion) { $questions[] = $currentQuestion; } return $questions; } ?>
  10. Olumide

    CBT Exam

    You are very intelligent sir. I saved the document as an html file and it created a folder as you have said. But my problem is, how do extract them? Here is my upload.php which extract text directly from Microsoft word but not extracting images or equation. <?php session_start(); if ($_SESSION['role'] !== 'teacher') { header("Location: login.php"); exit(); } require 'db.php'; require 'vendor/autoload.php'; // Load the PHPWord library use PhpOffice\PhpWord\IOFactory; $database = new Database(); $db = $database->getConnection(); if ($_SERVER['REQUEST_METHOD'] == 'POST') { $class_id = $_POST['class_id']; $subject_id = $_POST['subject_id']; $file = $_FILES['file']; if ($file['type'] == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { $upload_dir = 'uploads/'; $file_path = $upload_dir . basename($file['name']); if (move_uploaded_file($file['tmp_name'], $file_path)) { $phpWord = IOFactory::load($file_path, 'Word2007'); if ($phpWord) { echo "File loaded successfully.<br>"; } else { echo "Failed to load file.<br>"; exit(); } $questions = []; $currentQuestion = null; foreach ($phpWord->getSections() as $section) { foreach ($section->getElements() as $element) { if (method_exists($element, 'getText')) { $text = trim($element->getText()); // Convert bold text $text = preg_replace('/\*\*(.*?)\*\*/', '<strong>$1</strong>', $text); // Convert italicized text $text = preg_replace('/__(.*?)__/', '<em>$1</em>', $text); // Convert quoted text to blockquote $text = preg_replace('/\"(.*?)\"/', '<blockquote>$1</blockquote>', $text); // Convert subscript text $text = preg_replace('/~(.*?)~/', '<sub>$1</sub>', $text); // Convert superscript text $text = preg_replace('/\^(.*?)\^/', '<sup>$1</sup>', $text); if (strpos($text, 'Question:') === 0) { if ($currentQuestion) { $questions[] = $currentQuestion; } $currentQuestion = [ 'question_text' => substr($text, 10), 'options' => [], 'correct_option' => '' ]; } elseif (strpos($text, 'A)') === 0 || strpos($text, 'B)') === 0 || strpos($text, 'C)') === 0 || strpos($text, 'D)') === 0) { if ($currentQuestion) { $optionKey = strtolower($text[0]); $currentQuestion['options'][$optionKey] = substr($text, 3); } } elseif (strpos($text, 'Answer:') === 0) { if ($currentQuestion) { $currentQuestion['correct_option'] = trim(substr($text, 7)); } } } elseif (method_exists($element, 'getDrawing')) { // Handle image extraction $drawing = $element->getDrawing(); $imagePath = $drawing->getPath(); $imageName = basename($imagePath); $newImagePath = $upload_dir . $imageName; copy($imagePath, $newImagePath); // Here you can store $newImagePath in the database for the question } } } if ($currentQuestion) { $questions[] = $currentQuestion; } foreach ($questions as $question) { $stmt = $db->prepare("INSERT INTO questions (question_text, question_type, option_a, option_b, option_c, option_d, correct_option, subject_id) VALUES (?, 'Multiple Choice', ?, ?, ?, ?, ?, ?)"); if ($stmt->execute([ $question['question_text'], $question['options']['a'] ?? '', $question['options']['b'] ?? '', $question['options']['c'] ?? '', $question['options']['d'] ?? '', $question['correct_option'], $subject_id ])) { echo "Question inserted successfully: " . $question['question_text'] . "<br>"; } else { echo "Failed to insert question: " . $question['question_text'] . "<br>"; print_r($stmt->errorInfo()); } } echo "File uploaded and questions added successfully."; } else { echo "Failed to upload file."; } } else { echo "Invalid file type. Please upload a .docx file."; } } else { $classes = $db->query("SELECT * FROM classes")->fetchAll(PDO::FETCH_ASSOC); ?> <!DOCTYPE html> <html> <head> <title>Upload Questions</title> </head> <body> <h1>Upload Questions</h1> <form action="upload.php" method="post" enctype="multipart/form-data"> <label for="class">Class:</label> <select name="class_id" id="class" onchange="fetchSubjects(this.value)"> <option value="">Select Class</option> <?php foreach ($classes as $class): ?> <option value="<?= $class['class_id'] ?>"><?= $class['class_name'] ?></option> <?php endforeach; ?> </select> <label for="subject">Subject:</label> <select name="subject_id" id="subject"> <option value="">Select Subject</option> <!-- Subjects will be populated based on selected class --> </select> <label for="file">Choose a file:</label> <input type="file" name="file" id="file" accept=".docx"> <button type="submit">Upload</button> </form> <script> function fetchSubjects(classId) { fetch(`fetch_subjects.php?class_id=${classId}`) .then(response => response.json()) .then(data => { const subjectSelect = document.getElementById('subject'); subjectSelect.innerHTML = '<option value="">Select Subject</option>'; data.forEach(subject => { const option = document.createElement('option'); option.value = subject.subject_id; option.textContent = subject.subject_name; subjectSelect.appendChild(option); }); }); } </script> </body> </html> <?php } ?>
  11. Olumide

    CBT Exam

    Thank you sir. Can you please explain this further. In my previous and abandoned work, I do upload images inside the Microsoft word but and also wrote a script to identify that, but it doesn't work. It only captured the text documents.
  12. Olumide

    CBT Exam

    Dear gurus in the house, I am trying to create an online CBT examination such that admin should be able to upload questions directly from Ms Word which might involve equations, graphs, formatting like bold, italicize and other features. But I am very confused regards this because I am not a guru and I don't want to use ready made software like moodle, but I want to design mine from scratch as a web application. I have used pho with no framework and was able to extract questions from Microsoft word with auto grading but this is not extracting the images, equations and the formats like bold. I stumbled from there again to use python with fastapi but all to no avail. Please guru in the house help me by putting me through on the model to use to achieve this. The questions will uploaded based on class like Basic 1, 2 and so on. I know you are all intelligent and here is a forum that solve problems.
  13. Thanks so much.
  14. Hi, I am a newbie to Laravel and trying to familiarize myself with it, so I am starting with a CBT project and when I navigate to http://127.0.0.1:8000/, it keeps taking me to laravel news page which differ from my project page. Here is my web.php <?php use Illuminate\Foundation\Application; use Illuminate\Support\Facades\Route; use Inertia\Inertia; use App\Http\Controllers\QuestionController; Route::get('questions', [QuestionController::class, 'index'])->name('questions.index'); Route::get('questions/create', [QuestionController::class, 'create'])->name('questions.create'); Route::post('questions', [QuestionController::class, 'store'])->name('questions.store'); Route::get('/', function () { return Inertia::render('Welcome', [ 'canLogin' => Route::has('login'), 'canRegister' => Route::has('register'), 'laravelVersion' => Application::VERSION, 'phpVersion' => PHP_VERSION, ]); }); Route::middleware([ 'auth:sanctum', config('jetstream.auth_session'), 'verified', ])->group(function () { Route::get('/dashboard', function () { return Inertia::render('Dashboard'); })->name('dashboard'); }); And here is the page I keeps loading to Please advise and if I could get a comprehensive tutorial that will guide me to pass the hurdles. I find it a bit difficult but with its authentication built in, it is okay.
  15. I have an admission application form, and I want an email and SMS notification to be sent to the applicant's email and phone upon successful registration. I have tried using the mail() function, but I am not receiving any email notifications at the recipient's email. I also tried using Gmail, but none of them are working. I used Composer to install the vendor autoload, but it is still not working. I don't know where the error is coming from. Below are the tables and the code: Table CREATE TABLE applicants ( id INT AUTO_INCREMENT PRIMARY KEY, application_id VARCHAR(20) UNIQUE, surname VARCHAR(100), othername VARCHAR(100), dob DATE, phone VARCHAR(20), email VARCHAR(100), lga VARCHAR(100), state_origin VARCHAR(100), current_school VARCHAR(100), current_class VARCHAR(50), proposed_class ENUM('Year 7 (JSS1)', 'Year 8 (JSS2)', 'Year 10 (SS1)', 'Year 11 (SS2)', 'Year 1 (PRY 1)', 'Year 2 (PRY 2)', 'Year 3 (PRY 3)', 'Year 4 (PRY 4)'), status VARCHAR(20) DEFAULT 'Pending' ); CREATE TABLE exam_dates ( id INT AUTO_INCREMENT PRIMARY KEY, exam_date DATE, type ENUM('Online', 'On-site'), is_past BOOLEAN DEFAULT FALSE ); CREATE TABLE exam_registrations ( id INT AUTO_INCREMENT PRIMARY KEY, applicant_id INT, exam_date_id INT, FOREIGN KEY (applicant_id) REFERENCES applicants(id), FOREIGN KEY (exam_date_id) REFERENCES exam_dates(id) ); ALTER TABLE applicants ADD registered_on DATETIME; PHP <?php session_start(); require_once('db_config.php'); //require_once(__DIR__ . '/../vendor/autoload.php'); require_once('vendor/autoload.php'); // Import PHPMailer classes into the global namespace use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception; // Fetch available exam dates from the database try { $stmt = $pdo->query("SELECT * FROM exam_dates WHERE is_past = FALSE"); $exam_dates = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch(PDOException $e) { echo "Error: " . $e->getMessage(); } // Check if the user is logged in as an applicant if (!isset($_SESSION['user_type']) || $_SESSION['user_type'] !== 'applicant') { header("Location: login.php"); exit(); } // Logout logic if (isset($_POST['logout'])) { session_destroy(); // Destroy all session data header("Location: login.php"); // Redirect to the login page exit(); } // Set the applicant_id from the applicants table try { $username = $_SESSION['username']; $stmt = $pdo->prepare("SELECT * FROM applicants WHERE application_id = ?"); $stmt->execute([$username]); $applicant = $stmt->fetch(PDO::FETCH_ASSOC); $_SESSION['applicant_id'] = $applicant['id']; $applicant_email = $applicant['email']; // Store the applicant's email address } catch(PDOException $e) { echo "Error: " . $e->getMessage(); exit(); } // Check if the applicant has already registered for an exam date try { $stmt = $pdo->prepare("SELECT * FROM exam_registrations WHERE applicant_id = ?"); $stmt->execute([$applicant['id']]); $registered_exam_dates = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch(PDOException $e) { echo "Error: " . $e->getMessage(); } // Handle exam registration if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['exam_date'])) { // Check if the applicant has already registered for an exam date if (!empty($registered_exam_dates)) { //echo "You have already registered for an exam date."; exit(); } $applicant_id = $_SESSION['applicant_id']; $exam_date_id = $_POST['exam_date']; // Insert exam registration details into exam_registrations table try { $stmt = $pdo->prepare("INSERT INTO exam_registrations (applicant_id, exam_date_id) VALUES (?, ?)"); $stmt->execute([$applicant_id, $exam_date_id]); // Redirect to exam confirmation page or show success message header("Location: exam_confirmation.php"); // Send SMS to applicant $message = "Thank you for your application. Your username is: " . $applicant['application_id'] . " and Password is your surname."; $api_url = 'my api url'; $token = 'my token'; $sender = 'Olu'; $recipient = $applicant['phone']; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_URL => $api_url, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => 30, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_POSTFIELDS => array( 'token' => $token, 'sender' => $sender, 'to' => $recipient, 'message' => $message, ), )); $response = curl_exec($curl); $err = curl_error($curl); curl_close($curl); if ($err) { echo 'cURL Error #:' . $err; } else { echo 'SMS sent successfully.'; } // Send email notification to applicant using PHPMailer sendEmailNotification($applicant_email, $applicant['application_id'], $applicant['surname']); exit(); } catch(PDOException $e) { echo "Error: " . $e->getMessage(); } } // Function to send email notification using PHPMailer function sendEmailNotification($recipientEmail, $applicationId, $surname) { // Instantiate PHPMailer $mail = new PHPMailer(true); try { // SMTP configuration (Gmail) $mail->isSMTP(); $mail->Host = 'smtp.gmail.com'; $mail->SMTPAuth = true; $mail->Username = 'olu@gmail.com'; $mail->Password = 'mypassword'; $mail->SMTPSecure = 'tls'; $mail->Port = 587; // Email content $mail->setFrom('olu@gmail.com', 'My Schol'); $mail->addAddress($recipientEmail); $mail->Subject = 'Application Registration Confirmation'; $mail->isHTML(true); $mail->Body = "Dear $surname,<br><br>Thank you for your application at my Schol. Your username is: $applicationId and Password is your surname.<br><br>Best regards,<br>My Schol"; // Send email $mail->send(); echo 'Email sent successfully!'; } catch (Exception $e) { echo "Error: {$mail->ErrorInfo}"; } } ?> <!DOCTYPE html> <html> <head> <title>Applicant Dashboard</title> <script> // JavaScript function to display a popup function displayPopup() { alert("You have already registered for an exam date."); } </script> </head> <body> <h2>Welcome, <?php echo $applicant['surname']; ?></h2> <!-- Display applicant information --> <p>Applicant's Name: <?php echo $applicant['surname'] . ' ' . $applicant['othername']; ?></p> <p>Application ID: <?php echo $applicant['application_id']; ?></p> <p>Date of Birth: <?php echo $applicant['dob']; ?></p> <p>Phone: <?php echo $applicant['phone']; ?></p> <p>Email: <?php echo $applicant['email']; ?></p> <p>Local Government Area: <?php echo $applicant['lga']; ?></p> <p>State of Origin: <?php echo $applicant['state_origin']; ?></p> <p>Current School: <?php echo $applicant['current_school']; ?></p> <p>Current Class: <?php echo $applicant['current_class']; ?></p> <p>Proposed Class: <?php echo $applicant['proposed_class']; ?></p> <!-- Selected exam date --> <p>Selected Exam Date: <?php if (!empty($registered_exam_dates)) { $selected_exam_date_id = $registered_exam_dates[0]['exam_date_id']; foreach ($exam_dates as $date) { if ($date['id'] == $selected_exam_date_id) { echo date('F j, Y', strtotime($date['exam_date'])) . " (" . $date['type'] . ")"; break; } } } else { echo 'Not selected'; } ?> </p> <hr> <!-- Exam date selection --> <h3>Exam Date Selection</h3> <form id="exam_form" method="post"> <label for="exam_date">Select Exam Date:</label><br> <select id="exam_date" name="exam_date"> <?php foreach ($exam_dates as $date) : ?> <option value="<?php echo $date['id']; ?>"><?php echo $date['exam_date'] . " (" . $date['type'] . ")"; ?></option> <?php endforeach; ?> </select><br><br> <input type="button" value="Register for Exam" onclick="checkExamRegistration()"> <!--<input type="submit" value="Register for Exam">--> </form> <!-- Application status --> <h3>Application Status</h3> <p>Status: <?php echo $applicant['status']; ?></p> <!-- Logout form --> <form method="post"> <input type="submit" name="logout" value="Logout"> </form> <script> // Function to check if the applicant has already registered function checkExamRegistration() { <?php if (!empty($registered_exam_dates)): ?> displayPopup(); <?php else: ?> document.getElementById("exam_form").submit(); <?php endif; ?> } </script> </body> </html>
  16. I understand your point, for instance storing names again in the invoice table when the names are available in the student table is like exhausting more spaces and I don't think it can last if the storage space is not much, it can crash. You and @Barand have contributed tremendously. I am not yet an expert like you all, but I believe in consistent practicing.
  17. The fee is not constant, it do change, and for instance, a student who lives in hostel (Boarder) might decide he want to be coming from home as Day student, and a Day student can even decide he want to stay in hostel for a week maybe for a purpose known to him, and this do cause confusion if there is fixed price. Also, some parents might decide they want to pay less on any of the fee either IGCSE, Sports or anything. That is why I think entering the fee manually could be okay to handle such scenario. Some students are on scholarship where tuition will be free, but they will pay for other expenses and some on free feeding, and so on. And School fee may change anytime.
  18. Thanks @BarandCan you please give me more hints on this?
  19. total_fee_payable is still not calculating well but calculated_total_fee calculated well as required. I want this calculated as: total_fee_payable = (fee_amount + total_fee_from_items) - (total_fee_from_items + discount_amount) I have tried many approach but not working, it is not adding the amounts in the items_table to the total_fee_payable in the invoices table. From the sample query below: From items_table: invoice_id 118 should be added to the invoices id total_fee_payable CREATE TABLE `invoices` ( `id` int(11) NOT NULL, `student_id` int(11) NOT NULL, `semester_id` int(11) NOT NULL, `student_name` varchar(255) NOT NULL, `current_class_id` varchar(255) DEFAULT NULL, `fee_description` varchar(255) NOT NULL, `fee_amount` decimal(10,2) NOT NULL, `less_fee_description` varchar(255) DEFAULT NULL, `less_fee_amount` decimal(10,2) DEFAULT NULL, `discount_amount` decimal(10,2) DEFAULT NULL, `total_fee_payable` decimal(10,2) NOT NULL, `invoice_number` varchar(50) NOT NULL, `status` enum('PAID','PENDING') DEFAULT 'PENDING', `created_at` timestamp NOT NULL DEFAULT current_timestamp(), `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- -- Dumping data for table `invoices` -- INSERT INTO `invoices` (`id`, `student_id`, `semester_id`, `student_name`, `current_class_id`, `fee_description`, `fee_amount`, `less_fee_description`, `less_fee_amount`, `discount_amount`, `total_fee_payable`, `invoice_number`, `status`, `created_at`, `updated_at`) VALUES (118, 678, 16, 'AU', 'Year 11', 'school fee ', '50000.00', 'hg', '1000.00', '1000.00', '48000.00', 'B/2024/67982', 'PENDING', '2024-02-03 00:12:06', '2024-02-03 00:12:06'); -- -- Triggers `invoices` -- DELIMITER $$ CREATE TRIGGER `calculate_total_fee_payable_update` BEFORE UPDATE ON `invoices` FOR EACH ROW BEGIN SET NEW.total_fee_payable = ( SELECT COALESCE(SUM(fee_amount), 0) FROM items_table WHERE invoice_id = NEW.id ) + NEW.fee_amount - COALESCE((NEW.less_fee_amount + NEW.discount_amount), 0); END $$ DELIMITER ; ALTER TABLE `invoices` ADD PRIMARY KEY (`id`), ADD KEY `semester_id` (`semester_id`), ADD KEY `fk_invoices_student` (`student_id`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `invoices` -- ALTER TABLE `invoices` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=119; -- -- Constraints for dumped tables -- -- -- Constraints for table `invoices` -- ALTER TABLE `invoices` ADD CONSTRAINT `fk_invoices_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`), ADD CONSTRAINT `invoices_ibfk_1` FOREIGN KEY (`semester_id`) REFERENCES `semester` (`id`); COMMIT; CREATE TABLE `items_table` ( `id` int(11) NOT NULL, `invoice_id` int(11) NOT NULL, `fee_description` varchar(255) NOT NULL, `fee_amount` decimal(10,2) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; INSERT INTO `items_table` (`id`, `invoice_id`, `fee_description`, `fee_amount`) VALUES (16, 118, 'sports', '5000.00'), (17, 118, 'club', '5000.00'); ALTER TABLE `items_table` ADD PRIMARY KEY (`id`), ADD KEY `fk_items_invoice` (`invoice_id`); -- -- AUTO_INCREMENT for dumped tables -- -- -- AUTO_INCREMENT for table `items_table` -- ALTER TABLE `items_table` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=18; -- -- Constraints for dumped tables -- -- -- Constraints for table `items_table` -- ALTER TABLE `items_table` ADD CONSTRAINT `fk_items_invoice` FOREIGN KEY (`invoice_id`) REFERENCES `invoices` (`id`); COMMIT;
  20. I realized my mistake. You elders are all amazing. I don't know which one I should marked as solutions but you all did very excellent and expedite my problem to be solved. However, I am still working on it as this is just a section and when I am finally done with this, I have a vital question to ask on PHP as someone told me when I went for an interview that in the next two years, PHP will be outdated and not sellable in the market again. error_reporting(E_ALL); ini_set('display_errors', 1); // Check if the form data is set if ( isset($_POST['semesterId']) && isset($_POST['studentName']) && isset($_POST['feeDescription']) && isset($_POST['feeAmount']) && isset($_POST['lessFeeDescription']) && isset($_POST['lessFeeAmount']) && isset($_POST['discountAmount']) && isset($_POST['invoiceNumber']) //I think the problem is adding the grandTotal variable here ) { // Get form data $semesterId = $_POST['semesterId']; $studentName = $_POST['studentName']; $feeDescription = $_POST['feeDescription']; $feeAmount = $_POST['feeAmount']; $lessFeeDescription = $_POST['lessFeeDescription']; $lessFeeAmount = $_POST['lessFeeAmount']; $discountAmount = $_POST['discountAmount']; $invoiceNumber = $_POST['invoiceNumber']; // Calculate grand total $grandTotal = $feeAmount - ($lessFeeAmount + $discountAmount); // Additional validation if (empty($_POST['studentName']) || empty($_POST['feeDescription']) || empty($_POST['feeAmount'])) { header('HTTP/1.1 400 Bad Request'); echo 'Invalid form data. Please fill out all required fields.'; exit; } try { $query = "INSERT INTO invoices (semester_id, student_name, fee_description, fee_amount, less_fee_description, less_fee_amount, discount_amount, invoice_number, total_fee_payable) VALUES (:semesterId, :studentName, :feeDescription, :feeAmount, :lessFeeDescription, :lessFeeAmount, :discountAmount, :invoiceNumber, :totalFeePayable)"; // Prepare the query $stmt = $pdo->prepare($query); // Bind parameters $stmt->bindParam(':semesterId', $semesterId, PDO::PARAM_INT); $stmt->bindParam(':studentName', $studentName, PDO::PARAM_STR); $stmt->bindParam(':feeDescription', $feeDescription, PDO::PARAM_STR); $stmt->bindParam(':feeAmount', $feeAmount, PDO::PARAM_STR); $stmt->bindParam(':lessFeeDescription', $lessFeeDescription, PDO::PARAM_STR); $stmt->bindParam(':lessFeeAmount', $lessFeeAmount, PDO::PARAM_STR); $stmt->bindParam(':discountAmount', $discountAmount, PDO::PARAM_STR); $stmt->bindParam(':invoiceNumber', $invoiceNumber, PDO::PARAM_STR); $stmt->bindParam(':totalFeePayable', $grandTotal, PDO::PARAM_STR); // Bind grandTotal to the total_fee_payable column
  21. I am on it right now, could you believe I only modified the save_invoice.php script and got working, but I have removed all the calculations in javascript and still not working until now, but I want to retrace my steps again. You guys (no, I won't call you guys, but elders) are amazing and wonderful family. I am still on it, and will surely be here when there is hurdles again. Thanks for now.
  22. I will try to add the echo and see the output.
  23. Hmmm, this is very deep, I am not an expert in programming but learn everyday. The script work fine before not until I wanted to be inserting the grandTotal under fee_total_payable. And I have comment out the grandTotal again from all my code but still stopped working. From the console network tab, attached is the picture
  24. I have this html form which worked, but I added grandTotal to populate its column "total_fee_payable" in the table. When I added this, I altered the javascript and php code to insert the data into the database. Suddenly, it stopped working and when I checked the console log, it shows data saved successfully. What could be the problem please? Here is part of my html form <div id="student-details" class="w3-margin-top w3-margin-bottom"> <h2>Student Details</h2> <!-- Custom label and input container --> <div class="w3-row-padding"> <div class="w3-half w3-padding"> <label for="student-name">Name:</label> <input type="text" id="student-name" name="studentName" class="w3-input w3-border" disabled> </div> <div class="w3-half w3-padding"> <label for="current-class">Current Class:</label> <input type="text" id="current-class" name="currentClass" class="w3-input w3-border" disabled> </div> <div class="w3-half w3-padding"> <label for="selected-semester">Selected Semester:</label> <input type="text" id="selected-semester" name="selectedSemester" class="w3-input w3-border" disabled> </div> <div class="w3-half w3-padding"> <label for="invoice-number">Invoice Number:</label> <input type="text" id="invoice-number" name="invoiceNumber" class="w3-input w3-border" disabled> </div> <div class="w3-half w3-padding"> <label for="item">Fee Description:</label> <input type="text" id="item" name="feeDescription" class="w3-input w3-border" autocomplete="off"> </div> <div class="w3-half w3-padding"> <label for="price">Amount:</label> <input type="number" id="price" name="feeAmount" class="w3-input w3-border" autocomplete="off"> </div> <div class="w3-half w3-padding"> <label for="lessFee">Enter Less Fee Description:</label> <input type="text" id="fee_description" name="less_fee_description" class="w3-input w3-border" autocomplete="off"> </div> <div class="w3-half w3-padding"> <label for="priceAmount">Amount:</label> <input type="number" id="fee_amount" name="less_fee_amount" class="w3-input w3-border" autocomplete="off"> </div> <div class="w3-half w3-padding"> <label for="price">Discount Amount:</label> <input type="number" id="discount_amount" name="discount_amount" class="w3-input w3-border" autocomplete="off"> </div> </div> </div> <h2 class="w3-margin-bottom">Analysis</h2> <div class="w3-responsive"> <table class="w3-table w3-hoverable w3-bordered w3-striped" id="order-item-tbl"> <tfoot> <tr class="bg-gradient bg-dark-subtle bg-opacity-50"> <th class="bg-transparent w3-center w3-border" colspan="2">Sub-Total</th> <th class="bg-transparent w3-border w3-right-align" id="subTotalText">0</th> </tr> <tr class="bg-gradient bg-dark-subtle bg-opacity-50"> <th class="bg-transparent w3-center w3-border" colspan="2">Grand Total</th> <th class="bg-transparent w3-border w3-right-align" id="grandTotal" name="grandTotal">0</th> </tr> </tfoot> </table> </div><br> <button type="button" id="order-form-submit" class="w3-button w3-blue w3-block"><i class="far fa-plus-square"> </i> Add Invoice</button> </div> Here is the javascript // Function to update the grand total based on the formula function updateGrandTotal() { // Get values from the form var price = parseFloat(document.getElementById('price').value) || 0; var feeAmount = parseFloat(document.getElementById('fee_amount').value) || 0; var discountAmount = parseFloat(document.getElementById('discount_amount').value) || 0; // Calculate grand total var grandTotal = price - (feeAmount + discountAmount); // Update the grand total element document.getElementById('grandTotal').textContent = grandTotal.toFixed(2); } // Attach the update function to input events document.getElementById('price').addEventListener('input', updateGrandTotal); document.getElementById('fee_amount').addEventListener('input', updateGrandTotal); document.getElementById('discount_amount').addEventListener('input', updateGrandTotal); // Function to save invoice data function saveInvoiceData() { var semesterId = $('#semester').val(); var studentName = $('#student-name').val(); var feeDescription = $('#item').val(); var feeAmount = $('#price').val(); var lessFeeDescription = $('#fee_description').val(); var lessFeeAmount = $('#fee_amount').val(); var discountAmount = $('#discount_amount').val(); //var grandTotal = $('#grandTotal').val(); var invoiceNumber = $('#invoice-number').val(); // Make an AJAX request to save invoice data $.ajax({ url: 'invoice.php', method: 'POST', data: { semesterId: semesterId, studentName: studentName, feeDescription: feeDescription, feeAmount: feeAmount, lessFeeDescription: lessFeeDescription, lessFeeAmount: lessFeeAmount, discountAmount: discountAmount, //grandTotal: grandTotal, invoiceNumber: invoiceNumber }, success: function (response) { // Handle success response if needed console.log('Invoice data saved successfully.'); }, error: function (error) { console.error('Error:', error); } }); } // Bind the function to "Save" button $('#order-form-submit').on('click', function () { saveInvoiceData(); }); }); Here is the invoice.php // Check if the form data is set if ( isset($_POST['semesterId']) && isset($_POST['studentName']) && isset($_POST['feeDescription']) && isset($_POST['feeAmount']) && isset($_POST['lessFeeDescription']) && isset($_POST['lessFeeAmount']) && isset($_POST['discountAmount'])&& //isset($_POST['grandTotal'])&& isset($_POST['invoiceNumber']) ) { // Get form data $semesterId = $_POST['semesterId']; $studentName = $_POST['studentName']; $feeDescription = $_POST['feeDescription']; $feeAmount = $_POST['feeAmount']; $lessFeeDescription = $_POST['lessFeeDescription']; $lessFeeAmount = $_POST['lessFeeAmount']; $discountAmount = $_POST['discountAmount']; //$grandTotal = floatval($_POST['grandTotal']); $invoiceNumber = $_POST['invoiceNumber']; // Additional validation if (empty($_POST['studentName']) || empty($_POST['feeDescription']) || empty($_POST['feeAmount'])) { header('HTTP/1.1 400 Bad Request'); echo 'Invalid form data. Please fill out all required fields.'; exit; } try { $query = "INSERT INTO invos (semester_id, student_name, fee_description, fee_amount, less_fee_description, less_fee_amount, discount_amount, invoice_number) VALUES (:semesterId, :studentName, :feeDescription, :feeAmount, :lessFeeDescription, :lessFeeAmount, :discountAmount, :invoiceNumber)"; // Prepare the query $stmt = $pdo->prepare($query); // Bind parameters $stmt->bindParam(':semesterId', $semesterId, PDO::PARAM_INT); $stmt->bindParam(':studentName', $studentName, PDO::PARAM_STR); $stmt->bindParam(':feeDescription', $feeDescription, PDO::PARAM_STR); $stmt->bindParam(':feeAmount', $feeAmount, PDO::PARAM_STR); $stmt->bindParam(':lessFeeDescription', $lessFeeDescription, PDO::PARAM_STR); $stmt->bindParam(':lessFeeAmount', $lessFeeAmount, PDO::PARAM_STR); $stmt->bindParam(':discountAmount', $discountAmount, PDO::PARAM_STR); //$stmt->bindParam(':grandTotal', $grandTotal, PDO::PARAM_STR); $stmt->bindParam(':invoiceNumber', $invoiceNumber, PDO::PARAM_STR); try { // ... // Output the executed query for debugging echo $stmt->queryString; // Execute the query $stmt->execute(); $rowCount = $stmt->rowCount(); if ($rowCount > 0) { echo 'Invoice data saved successfully.'; } else { echo 'No rows affected. Check for errors.'; } } catch (PDOException $e) { // Handle database errors echo 'Error: ' . $e->getMessage(); }
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.