Path : /home/vishqocm/pcib.in/admin/
File Upload :
Current File : /home/vishqocm/pcib.in/admin/import_questions.php

<?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();
}

// Check if form is submitted
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['bank_id']) || !isset($_FILES['question_file'])) {
    $_SESSION['error'] = "Invalid request.";
    header("Location: banks.php");
    exit();
}

$bank_id = intval($_POST['bank_id']);

// Validate question bank exists
$check_bank = $conn->prepare("SELECT id FROM question_banks WHERE id = ?");
$check_bank->bind_param("i", $bank_id);
$check_bank->execute();
$bank_exists = $check_bank->get_result()->num_rows > 0;
$check_bank->close();

if (!$bank_exists) {
    $_SESSION['error'] = "Question bank not found.";
    header("Location: banks.php");
    exit();
}

// Check file upload
$file = $_FILES['question_file'];
if ($file['error'] !== UPLOAD_ERR_OK) {
    $_SESSION['error'] = "Error uploading file: " . getUploadErrorMessage($file['error']);
    header("Location: questions.php?bank_id=" . $bank_id);
    exit();
}

// Validate file type
$file_ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowed_extensions = ['csv', 'xlsx', 'xls'];

if (!in_array($file_ext, $allowed_extensions)) {
    $_SESSION['error'] = "Only CSV and Excel files are allowed.";
    header("Location: questions.php?bank_id=" . $bank_id);
    exit();
}

// Process file based on extension
$questions = [];
$errors = [];

if ($file_ext === 'csv') {
    // Process CSV file
    $handle = fopen($file['tmp_name'], 'r');
    
    if ($handle !== false) {
        // Read header line
        $header = fgetcsv($handle);
        
        // Normalize header fields
        $header = array_map('trim', $header);
        $header = array_map('strtolower', $header);
        
        // Required fields
        $required_fields = ['question_text', 'question_type', 'difficulty', 'marks'];
        
        // Check if required fields exist
        foreach ($required_fields as $field) {
            if (!in_array($field, $header)) {
                $errors[] = "Required column '$field' is missing in the CSV file.";
            }
        }
        
        if (empty($errors)) {
            // Get indexes for each column
            $column_indexes = [];
            foreach ($header as $index => $column_name) {
                $column_indexes[$column_name] = $index;
            }
            
            // Read data rows
            $row_number = 2; // Start from line 2 (after header)
            while (($row = fgetcsv($handle)) !== false) {
                $question = [
                    'question_text' => $row[$column_indexes['question_text']] ?? '',
                    'question_type' => strtolower($row[$column_indexes['question_type']] ?? ''),
                    'difficulty' => strtolower($row[$column_indexes['difficulty']] ?? ''),
                    'marks' => floatval($row[$column_indexes['marks']] ?? 0),
                    'is_practice' => isset($column_indexes['is_practice']) ? (strtolower($row[$column_indexes['is_practice']]) === 'yes' ? 1 : 0) : 0,
                    'explanation' => $row[$column_indexes['explanation']] ?? '',
                    'options' => [],
                    'answer_key' => $row[$column_indexes['answer_key']] ?? '',
                    'row_number' => $row_number
                ];
                
                // Add options for multiple choice
                if ($question['question_type'] === 'multiple_choice') {
                    $option_count = 1;
                    while (isset($column_indexes['option_' . $option_count]) && isset($row[$column_indexes['option_' . $option_count]])) {
                        $option_text = $row[$column_indexes['option_' . $option_count]];
                        if (!empty($option_text)) {
                            $is_correct = isset($column_indexes['correct_option_' . $option_count]) && 
                                         strtolower($row[$column_indexes['correct_option_' . $option_count]]) === 'yes';
                            
                            $question['options'][] = [
                                'option_text' => $option_text,
                                'is_correct' => $is_correct
                            ];
                        }
                        $option_count++;
                    }
                }
                // Handle true/false questions
                elseif ($question['question_type'] === 'true_false') {
                    $correct_answer = strtolower($row[$column_indexes['correct_answer']] ?? '');
                    if ($correct_answer === 'true' || $correct_answer === 'false') {
                        $question['correct_answer'] = $correct_answer;
                    } else {
                        $errors[] = "Row $row_number: True/False question must have 'true' or 'false' as correct_answer.";
                    }
                }
                
                // Validate question
                validateQuestion($question, $errors);
                
                $questions[] = $question;
                $row_number++;
            }
        }
        fclose($handle);
    } else {
        $errors[] = "Failed to open the CSV file.";
    }
} elseif ($file_ext === 'xlsx' || $file_ext === 'xls') {
    // For Excel files, we'll need a library like PhpSpreadsheet
    // This implementation uses SimpleXLSX as a lightweight alternative
    require_once '../vendor/SimpleXLSX.php';
    
    if (!class_exists('SimpleXLSX')) {
        $errors[] = "SimpleXLSX library not found. Excel import requires this library.";
    } else {
        $xlsx = SimpleXLSX::parse($file['tmp_name']);
        
        if ($xlsx) {
            $rows = $xlsx->rows();
            
            // First row is the header
            $header = array_map('trim', $rows[0]);
            $header = array_map('strtolower', $header);
            
            // Required fields
            $required_fields = ['question_text', 'question_type', 'difficulty', 'marks'];
            
            // Check if required fields exist
            foreach ($required_fields as $field) {
                if (!in_array($field, $header)) {
                    $errors[] = "Required column '$field' is missing in the Excel file.";
                }
            }
            
            if (empty($errors)) {
                // Get indexes for each column
                $column_indexes = [];
                foreach ($header as $index => $column_name) {
                    $column_indexes[$column_name] = $index;
                }
                
                // Process data rows (skip header)
                for ($i = 1; $i < count($rows); $i++) {
                    $row = $rows[$i];
                    $row_number = $i + 1;
                    
                    $question = [
                        'question_text' => $row[$column_indexes['question_text']] ?? '',
                        'question_type' => strtolower($row[$column_indexes['question_type']] ?? ''),
                        'difficulty' => strtolower($row[$column_indexes['difficulty']] ?? ''),
                        'marks' => floatval($row[$column_indexes['marks']] ?? 0),
                        'is_practice' => isset($column_indexes['is_practice']) ? (strtolower($row[$column_indexes['is_practice']]) === 'yes' ? 1 : 0) : 0,
                        'explanation' => $row[$column_indexes['explanation']] ?? '',
                        'options' => [],
                        'answer_key' => $row[$column_indexes['answer_key']] ?? '',
                        'row_number' => $row_number
                    ];
                    
                    // Add options for multiple choice
                    if ($question['question_type'] === 'multiple_choice') {
                        $option_count = 1;
                        while (isset($column_indexes['option_' . $option_count]) && isset($row[$column_indexes['option_' . $option_count]])) {
                            $option_text = $row[$column_indexes['option_' . $option_count]];
                            if (!empty($option_text)) {
                                $is_correct = isset($column_indexes['correct_option_' . $option_count]) && 
                                             strtolower($row[$column_indexes['correct_option_' . $option_count]]) === 'yes';
                                
                                $question['options'][] = [
                                    'option_text' => $option_text,
                                    'is_correct' => $is_correct
                                ];
                            }
                            $option_count++;
                        }
                    }
                    // Handle true/false questions
                    elseif ($question['question_type'] === 'true_false') {
                        $correct_answer = strtolower($row[$column_indexes['correct_answer']] ?? '');
                        if ($correct_answer === 'true' || $correct_answer === 'false') {
                            $question['correct_answer'] = $correct_answer;
                        } else {
                            $errors[] = "Row $row_number: True/False question must have 'true' or 'false' as correct_answer.";
                        }
                    }
                    
                    // Validate question
                    validateQuestion($question, $errors);
                    
                    $questions[] = $question;
                }
            }
        } else {
            $errors[] = "Failed to parse Excel file: " . SimpleXLSX::parseError();
        }
    }
}

// If there are validation errors, redirect back with error message
if (!empty($errors)) {
    $_SESSION['error'] = "Import failed. Please fix the following issues:<br>" . implode("<br>", $errors);
    header("Location: questions.php?bank_id=" . $bank_id);
    exit();
}

// No questions found
if (empty($questions)) {
    $_SESSION['error'] = "No valid questions found in the file.";
    header("Location: questions.php?bank_id=" . $bank_id);
    exit();
}

// Import the questions
$conn->begin_transaction();
$success_count = 0;
$error_count = 0;

try {
    foreach ($questions as $question) {
        // Insert question
        $insert_question = $conn->prepare("INSERT INTO questions (question_bank_id, question_text, question_type, difficulty, marks, is_practice, explanation) 
                                          VALUES (?, ?, ?, ?, ?, ?, ?)");
        $insert_question->bind_param("isssdis", 
            $bank_id, 
            $question['question_text'], 
            $question['question_type'], 
            $question['difficulty'], 
            $question['marks'], 
            $question['is_practice'], 
            $question['explanation']
        );
        $insert_question->execute();
        $question_id = $conn->insert_id;
        
        // Insert options based on question type
        if ($question['question_type'] === 'multiple_choice') {
            foreach ($question['options'] as $option) {
                $insert_option = $conn->prepare("INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, ?, ?)");
                $insert_option->bind_param("isi", $question_id, $option['option_text'], $option['is_correct']);
                $insert_option->execute();
            }
        } 
        elseif ($question['question_type'] === 'true_false') {
            // Insert true option
            $insert_option = $conn->prepare("INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, 'True', ?)");
            $is_true_correct = ($question['correct_answer'] === 'true') ? 1 : 0;
            $insert_option->bind_param("ii", $question_id, $is_true_correct);
            $insert_option->execute();
            
            // Insert false option
            $insert_option = $conn->prepare("INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, 'False', ?)");
            $is_false_correct = ($question['correct_answer'] === 'false') ? 1 : 0;
            $insert_option->bind_param("ii", $question_id, $is_false_correct);
            $insert_option->execute();
        }
        elseif ($question['question_type'] === 'short_answer' && !empty($question['answer_key'])) {
            // For short answer, store the answer key
            $insert_option = $conn->prepare("INSERT INTO question_options (question_id, option_text, is_correct) VALUES (?, ?, 1)");
            $insert_option->bind_param("is", $question_id, $question['answer_key']);
            $insert_option->execute();
        }
        
        $success_count++;
    }
    
    $conn->commit();
    $_SESSION['success'] = "$success_count questions imported successfully.";
    
} catch (Exception $e) {
    $conn->rollback();
    $_SESSION['error'] = "Error importing questions: " . $e->getMessage();
}

header("Location: questions.php?bank_id=" . $bank_id);
exit();

// Helper functions
function validateQuestion(&$question, &$errors) {
    $row_number = $question['row_number'];
    
    // Check required fields
    if (empty($question['question_text'])) {
        $errors[] = "Row $row_number: Question text is required.";
    }
    
    // Validate question type
    $valid_types = ['multiple_choice', 'true_false', 'short_answer', 'essay'];
    if (!in_array($question['question_type'], $valid_types)) {
        $errors[] = "Row $row_number: Invalid question type. Must be one of: " . implode(', ', $valid_types);
    }
    
    // Validate difficulty
    $valid_difficulties = ['easy', 'medium', 'hard'];
    if (!in_array($question['difficulty'], $valid_difficulties)) {
        $errors[] = "Row $row_number: Invalid difficulty. Must be one of: " . implode(', ', $valid_difficulties);
    }
    
    // Validate marks
    if ($question['marks'] <= 0) {
        $errors[] = "Row $row_number: Marks must be greater than 0.";
    }
    
    // Validate question type specific requirements
    if ($question['question_type'] === 'multiple_choice') {
        // Check if options exist
        if (empty($question['options'])) {
            $errors[] = "Row $row_number: Multiple choice questions must have options.";
        } else {
            // Check if at least one option is marked as correct
            $has_correct = false;
            foreach ($question['options'] as $option) {
                if ($option['is_correct']) {
                    $has_correct = true;
                    break;
                }
            }
            if (!$has_correct) {
                $errors[] = "Row $row_number: Multiple choice questions must have at least one correct option.";
            }
        }
    } elseif ($question['question_type'] === 'short_answer') {
        // Short answer questions should have an answer key
        if (empty($question['answer_key'])) {
            $errors[] = "Row $row_number: Short answer questions must have an answer key.";
        }
    }
}

function getUploadErrorMessage($error_code) {
    switch ($error_code) {
        case UPLOAD_ERR_INI_SIZE:
            return "The uploaded file exceeds the upload_max_filesize directive in php.ini.";
        case UPLOAD_ERR_FORM_SIZE:
            return "The uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form.";
        case UPLOAD_ERR_PARTIAL:
            return "The uploaded file was only partially uploaded.";
        case UPLOAD_ERR_NO_FILE:
            return "No file was uploaded.";
        case UPLOAD_ERR_NO_TMP_DIR:
            return "Missing a temporary folder.";
        case UPLOAD_ERR_CANT_WRITE:
            return "Failed to write file to disk.";
        case UPLOAD_ERR_EXTENSION:
            return "A PHP extension stopped the file upload.";
        default:
            return "Unknown upload error.";
    }
}