Add PDF Survey Importer with smart Thai questionnaire parser and preview editor
This commit is contained in:
@@ -0,0 +1,558 @@
|
||||
<?php
|
||||
/**
|
||||
* Import Survey from PDF API
|
||||
* POST ?action=parsePdf - Upload and parse PDF file to survey JSON
|
||||
* POST ?action=confirmImport - Save parsed survey JSON into database
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../config/database.php';
|
||||
require_once __DIR__ . '/../config/auth.php';
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Smalot\PdfParser\Parser;
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
requireLogin();
|
||||
}
|
||||
|
||||
if (isset($_GET['action'])) {
|
||||
$action = $_GET['action'] ?? '';
|
||||
switch ($action) {
|
||||
case 'parsePdf':
|
||||
parsePdf();
|
||||
break;
|
||||
case 'confirmImport':
|
||||
confirmImport();
|
||||
break;
|
||||
default:
|
||||
jsonResponse(['error' => 'Invalid action'], 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle PDF upload and extraction
|
||||
*/
|
||||
function parsePdf() {
|
||||
if (!isset($_FILES['pdf_file']) || $_FILES['pdf_file']['error'] !== UPLOAD_ERR_OK) {
|
||||
$errorMsg = 'กรุณาเลือกไฟล์ PDF';
|
||||
if (isset($_FILES['pdf_file']['error'])) {
|
||||
switch ($_FILES['pdf_file']['error']) {
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$errorMsg = 'ขนาดไฟล์ใหญ่เกินขีดจำกัดของเซิร์ฟเวอร์';
|
||||
break;
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$errorMsg = 'ไม่มีไฟล์ถูกอัปโหลด';
|
||||
break;
|
||||
}
|
||||
}
|
||||
jsonResponse(['error' => $errorMsg], 400);
|
||||
}
|
||||
|
||||
$file = $_FILES['pdf_file'];
|
||||
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if ($ext !== 'pdf') {
|
||||
jsonResponse(['error' => 'รองรับเฉพาะไฟล์เอกสาร PDF เท่านั้น'], 400);
|
||||
}
|
||||
|
||||
$tmpPath = $file['tmp_name'];
|
||||
|
||||
try {
|
||||
$parser = new Parser();
|
||||
$pdf = $parser->parseFile($tmpPath);
|
||||
$rawText = $pdf->getText();
|
||||
} catch (\Throwable $e) {
|
||||
jsonResponse(['error' => 'ไม่สามารถอ่านไฟล์ PDF ได้: ' . $e->getMessage()], 500);
|
||||
}
|
||||
|
||||
$rawText = trim($rawText);
|
||||
if (empty($rawText)) {
|
||||
jsonResponse(['error' => 'ไม่พบข้อความในไฟล์ PDF (อาจเป็นไฟล์สแกนรูปภาพล้วนที่ไม่มี Text Layer)'], 400);
|
||||
}
|
||||
|
||||
// Parse structured survey from raw text
|
||||
$parsedSurvey = parseThaiSurveyText($rawText, pathinfo($file['name'], PATHINFO_FILENAME));
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'message' => 'อ่านและวิเคราะห์โครงสร้างแบบประเมินจาก PDF สำเร็จ',
|
||||
'raw_text_preview' => mb_substr($rawText, 0, 500, 'UTF-8') . '...',
|
||||
'survey' => $parsedSurvey
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save finalized survey to DB
|
||||
*/
|
||||
function confirmImport() {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$projectId = !empty($input['project_id']) ? (int)$input['project_id'] : null;
|
||||
$surveyData = $input['survey_data'] ?? null;
|
||||
|
||||
if (!$surveyData || empty($surveyData['title'])) {
|
||||
jsonResponse(['error' => 'ข้อมูลแบบประเมินไม่ถูกต้อง หรือไม่มีชื่อแบบประเมิน'], 400);
|
||||
}
|
||||
|
||||
$db = getDB();
|
||||
$uniqueLink = bin2hex(random_bytes(16));
|
||||
|
||||
$db->beginTransaction();
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO surveys (user_id, project_id, title, description, unique_link, status) VALUES (?, ?, ?, ?, ?, 'active')");
|
||||
$stmt->execute([
|
||||
$_SESSION['user_id'] ?? 1,
|
||||
$projectId,
|
||||
$surveyData['title'],
|
||||
$surveyData['description'] ?? '',
|
||||
$uniqueLink
|
||||
]);
|
||||
$surveyId = $db->lastInsertId();
|
||||
|
||||
$sections = $surveyData['sections'] ?? [];
|
||||
$secNum = 1;
|
||||
|
||||
foreach ($sections as $sec) {
|
||||
$stmt = $db->prepare("INSERT INTO survey_sections (survey_id, section_number, title, description, category) VALUES (?, ?, ?, ?, ?)");
|
||||
$secCategory = $sec['category'] ?? 'rating';
|
||||
$stmt->execute([
|
||||
$surveyId,
|
||||
$secNum,
|
||||
$sec['title'] ?? ('ตอนที่ ' . $secNum),
|
||||
$sec['description'] ?? '',
|
||||
$secCategory
|
||||
]);
|
||||
$sectionId = $db->lastInsertId();
|
||||
|
||||
$orderNum = 1;
|
||||
|
||||
// Direct questions in this section
|
||||
if (!empty($sec['questions'])) {
|
||||
foreach ($sec['questions'] as $q) {
|
||||
insertQuestionRecord($db, $sectionId, null, $q, $orderNum++);
|
||||
}
|
||||
}
|
||||
|
||||
// Headings with sub-questions (e.g. ด้านวิทยากร, ด้านสถานที่)
|
||||
if (!empty($sec['headings'])) {
|
||||
$headingOrder = 1;
|
||||
foreach ($sec['headings'] as $h) {
|
||||
$stmtH = $db->prepare("INSERT INTO survey_headings (section_id, title, description, order_num) VALUES (?, ?, ?, ?)");
|
||||
$stmtH->execute([
|
||||
$sectionId,
|
||||
$h['title'] ?? ('ด้านที่ ' . $headingOrder),
|
||||
$h['description'] ?? '',
|
||||
$headingOrder++
|
||||
]);
|
||||
$headingId = $db->lastInsertId();
|
||||
|
||||
if (!empty($h['questions'])) {
|
||||
foreach ($h['questions'] as $hq) {
|
||||
insertQuestionRecord($db, $sectionId, $headingId, $hq, $orderNum++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$secNum++;
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
jsonResponse([
|
||||
'success' => true,
|
||||
'message' => 'สร้างแบบประเมินจาก PDF เรียบร้อยแล้ว',
|
||||
'survey_id' => $surveyId,
|
||||
'unique_link' => $uniqueLink,
|
||||
'title' => $surveyData['title']
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
$db->rollBack();
|
||||
jsonResponse(['error' => 'บันทึกข้อมูลไม่สำเร็จ: ' . $e->getMessage()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
function insertQuestionRecord($db, $sectionId, $headingId, $q, $orderNum) {
|
||||
$qText = trim($q['question_text'] ?? '');
|
||||
if (empty($qText)) return;
|
||||
|
||||
$qType = $q['question_type'] ?? 'radio';
|
||||
$isRequired = isset($q['is_required']) ? (int)$q['is_required'] : 1;
|
||||
$options = !empty($q['options']) && is_array($q['options']) ? json_encode(array_values($q['options']), JSON_UNESCAPED_UNICODE) : null;
|
||||
$scaleMin = $q['scale_min'] ?? 1;
|
||||
$scaleMax = $q['scale_max'] ?? 5;
|
||||
$desc = $q['description'] ?? '';
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO survey_questions (section_id, heading_id, question_text, question_type, is_required, order_num, options, scale_min, scale_max, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$sectionId,
|
||||
$headingId,
|
||||
$qText,
|
||||
$qType,
|
||||
$isRequired,
|
||||
$orderNum,
|
||||
$options,
|
||||
$scaleMin,
|
||||
$scaleMax,
|
||||
$desc
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intelligent Thai Survey Pattern Parser
|
||||
*/
|
||||
function parseThaiSurveyText($text, $defaultTitle = 'แบบประเมินความพึงพอใจ') {
|
||||
// 1. Normalize line endings and whitespace
|
||||
$text = str_replace(["\r\n", "\r"], "\n", $text);
|
||||
|
||||
// Replace special unicode bullets/boxes with standard marks
|
||||
$text = preg_replace('/[\x{25A0}\x{25A1}\x{25CB}\x{25CF}\x{2610}\x{2611}\x{274F}\x{25EF}]/u', '[ ]', $text);
|
||||
|
||||
$rawLines = explode("\n", $text);
|
||||
$lines = [];
|
||||
foreach ($rawLines as $line) {
|
||||
$trimmed = trim($line);
|
||||
if ($trimmed !== '') {
|
||||
$lines[] = $trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Extract Document Title and Description
|
||||
$title = '';
|
||||
$description = '';
|
||||
$startIndex = 0;
|
||||
$titleLines = [];
|
||||
|
||||
// Filter out standard scale headers/garbage lines like "5 4 3 2 1", "มากที่สุด มาก ปานกลาง..."
|
||||
$ignoredPatterns = [
|
||||
'/^ระดับความพึงพอใจ/u',
|
||||
'/^ระดับความคิดเห็น/u',
|
||||
'/^เกณฑ์การประเมิน/u',
|
||||
'/^5\s+4\s+3\s+2\s+1/u',
|
||||
'/^หมายเหตุ\s*:/u',
|
||||
'/^หน้า\s*\d+/u',
|
||||
'/^page\s*\d+/i'
|
||||
];
|
||||
|
||||
for ($i = 0; $i < min(8, count($lines)); $i++) {
|
||||
$line = $lines[$i];
|
||||
|
||||
// If we hit "ตอนที่" or "ส่วนที่" or "คำชี้แจง", stop title accumulation
|
||||
if (preg_match('/^(ตอนที่|ส่วนที่|คำชี้แจง|คำอธิบาย|ข้อชี้แจง)/u', $line)) {
|
||||
$startIndex = $i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (preg_match('/(แบบประเมิน|แบบสอบถาม|โครงการ|การอบรม|รายงานการประเมิน|แบบสำรวจ)/u', $line)) {
|
||||
$titleLines[] = $line;
|
||||
$startIndex = $i + 1;
|
||||
} elseif (empty($titleLines) && $i === 0) {
|
||||
$titleLines[] = $line;
|
||||
$startIndex = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$title = !empty($titleLines) ? implode(' ', $titleLines) : ($defaultTitle ?: 'แบบประเมินความพึงพอใจ');
|
||||
|
||||
// 3. Scan for "คำชี้แจง" (Description)
|
||||
for ($i = $startIndex; $i < min($startIndex + 6, count($lines)); $i++) {
|
||||
$line = $lines[$i];
|
||||
if (preg_match('/^(คำชี้แจง|คำอธิบาย|ข้อชี้แจง)[\s\:\.\-–]*(.*)/u', $line, $m)) {
|
||||
$description = trim($m[2]);
|
||||
if (empty($description) && isset($lines[$i + 1])) {
|
||||
$description = $lines[$i + 1];
|
||||
$startIndex = $i + 2;
|
||||
} else {
|
||||
$startIndex = $i + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Partition lines into Sections
|
||||
$sections = [];
|
||||
$currentSection = null;
|
||||
|
||||
for ($i = $startIndex; $i < count($lines); $i++) {
|
||||
$line = $lines[$i];
|
||||
|
||||
// Check if line is a Section Header: "ตอนที่ 1...", "ส่วนที่ 1...", "ตอนที่ 2...", "ตอนที่ 3..."
|
||||
if (preg_match('/^(ตอนที่|ส่วนที่)\s*([0-9๑-๙]+|หนึ่ง|สอง|สาม|สี่|ห้า)[\s\:\.\-–]*(.*)/u', $line, $matches)) {
|
||||
if ($currentSection !== null) {
|
||||
$sections[] = $currentSection;
|
||||
}
|
||||
|
||||
$rawSecNum = $matches[2];
|
||||
$secNumMap = ['หนึ่ง' => 1, 'สอง' => 2, 'สาม' => 3, 'สี่' => 4, '๑' => 1, '๒' => 2, '๓' => 3, '๔' => 4];
|
||||
$secNum = $secNumMap[$rawSecNum] ?? (int)$rawSecNum;
|
||||
if ($secNum <= 0) $secNum = count($sections) + 1;
|
||||
|
||||
$secTitle = trim($matches[0]);
|
||||
$secCategory = 'rating';
|
||||
|
||||
if (preg_match('/(ข้อมูลทั่วไป|สถานภาพ|ข้อมูลส่วนตัว|สถานะ)/u', $secTitle)) {
|
||||
$secCategory = 'general';
|
||||
} elseif (preg_match('/(ข้อเสนอแนะ|ความคิดเห็น|ความเห็นเพิ่มเติม|ข้อคิดเห็น)/u', $secTitle)) {
|
||||
$secCategory = 'suggestion';
|
||||
} elseif (preg_match('/(ความพึงพอใจ|ระดับความพึงพอใจ|การประเมินผล|เกณฑ์)/u', $secTitle)) {
|
||||
$secCategory = 'rating';
|
||||
}
|
||||
|
||||
$currentSection = [
|
||||
'section_number' => $secNum,
|
||||
'title' => $secTitle,
|
||||
'description' => '',
|
||||
'category' => $secCategory,
|
||||
'raw_lines' => []
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($currentSection === null) {
|
||||
// Default section 1 if not explicitly declared
|
||||
$currentSection = [
|
||||
'section_number' => 1,
|
||||
'title' => 'ตอนที่ 1 ข้อมูลทั่วไปและข้อประเมิน',
|
||||
'description' => '',
|
||||
'category' => 'general',
|
||||
'raw_lines' => []
|
||||
];
|
||||
}
|
||||
|
||||
$currentSection['raw_lines'][] = $line;
|
||||
}
|
||||
|
||||
if ($currentSection !== null) {
|
||||
$sections[] = $currentSection;
|
||||
}
|
||||
|
||||
// 5. Parse each section's contents (Headings, Questions, Options, Rating Scales)
|
||||
$parsedSections = [];
|
||||
$secCounter = 1;
|
||||
|
||||
foreach ($sections as $sec) {
|
||||
$secCategory = $sec['category'];
|
||||
$rawSecLines = $sec['raw_lines'];
|
||||
$headings = [];
|
||||
$questions = [];
|
||||
|
||||
// Check if section contains sub-headings (e.g. ด้านที่ 1..., 1. ด้าน..., 2. ด้าน...)
|
||||
$currentHeading = null;
|
||||
$currentQuestion = null;
|
||||
|
||||
for ($j = 0; $j < count($rawSecLines); $j++) {
|
||||
$line = $rawSecLines[$j];
|
||||
|
||||
// Ignore table scale legends / headers
|
||||
$shouldSkip = false;
|
||||
foreach ($ignoredPatterns as $pat) {
|
||||
if (preg_match($pat, $line)) {
|
||||
$shouldSkip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($shouldSkip) continue;
|
||||
|
||||
// Check if line is a Heading / Dimension:
|
||||
// "ด้านที่ 1 ด้านวิทยากร", "1. ด้านวิทยากร", "ด้านกระบวนการขั้นตอน", "1. ด้านเนื้อหา"
|
||||
if (preg_match('/^(ด้านที่\s*[0-9๑-๙]+|[0-9๑-๙]+\.\s*ด้าน|ด้าน[ก-๙]+)[\s\:\.\-–]*(.*)/u', $line) && !preg_match('/\[\s*\]/u', $line)) {
|
||||
// Finalize previous question
|
||||
if ($currentQuestion !== null) {
|
||||
if ($currentHeading !== null) {
|
||||
$currentHeading['questions'][] = $currentQuestion;
|
||||
} else {
|
||||
$questions[] = $currentQuestion;
|
||||
}
|
||||
$currentQuestion = null;
|
||||
}
|
||||
|
||||
if ($currentHeading !== null) {
|
||||
$headings[] = $currentHeading;
|
||||
}
|
||||
|
||||
$currentHeading = [
|
||||
'title' => $line,
|
||||
'description' => '',
|
||||
'questions' => []
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if line is a question
|
||||
// e.g. "1. เพศ", "1.1 วิทยากรมีความรู้", "2. สถานภาพ", "• ความเหมาะสมของสถานที่"
|
||||
// or starting with number: "1.1", "1.", "2)", "(1)"
|
||||
$isNumberedQuestion = preg_match('/^([0-9๑-๙]+\.[0-9๑-๙]+[\.\)]?\s*|[0-9๑-๙]+[\.\)]\s*|[\-\•\*]\s+)(.*)/u', $line, $qMatch);
|
||||
|
||||
// Or general question prompt: "เพศ :", "ระดับการศึกษา :", "ชั้นปี :"
|
||||
$isPromptQuestion = preg_match('/^(เพศ|สถานะ|สถานภาพ|อายุ|ระดับการศึกษา|ระดับชั้น|สาขาวิชา|แผนกวิชา|สังกัด|ตำแหน่ง|อาชีพ)[\s\:\.\-–]*/u', $line);
|
||||
|
||||
if ($isNumberedQuestion || ($isPromptQuestion && !preg_match('/\[\s*\]/u', $line))) {
|
||||
// Save previous question
|
||||
if ($currentQuestion !== null) {
|
||||
if ($currentHeading !== null) {
|
||||
$currentHeading['questions'][] = $currentQuestion;
|
||||
} else {
|
||||
$questions[] = $currentQuestion;
|
||||
}
|
||||
$currentQuestion = null;
|
||||
}
|
||||
|
||||
$qText = $isNumberedQuestion ? trim($qMatch[2]) : $line;
|
||||
// Remove numbering from question text for clean display if desired, or keep clear text
|
||||
if (empty($qText)) $qText = $line;
|
||||
|
||||
// Determine default question type based on section or content
|
||||
$qType = 'radio';
|
||||
$scaleMin = 1;
|
||||
$scaleMax = 5;
|
||||
$options = null;
|
||||
|
||||
if ($secCategory === 'suggestion') {
|
||||
$qType = 'text';
|
||||
} elseif ($secCategory === 'rating' || $currentHeading !== null || preg_match('/^[0-9๑-๙]+\.[0-9๑-๙]+/u', $line)) {
|
||||
$qType = 'radio';
|
||||
$options = null; // Rating scale 1-5
|
||||
} elseif ($secCategory === 'general') {
|
||||
$qType = 'radio';
|
||||
$options = [];
|
||||
}
|
||||
|
||||
// Check if options are inline on the same line (e.g. "[ ] ชาย [ ] หญิง")
|
||||
$inlineOptions = extractOptionsFromLine($line);
|
||||
if (!empty($inlineOptions)) {
|
||||
$options = $inlineOptions;
|
||||
// Clean question text of options
|
||||
$qText = preg_replace('/(\[\s*\]|\(\s*\)|[1-4]\)).*/u', '', $qText);
|
||||
$qText = trim($qText, " :.-–");
|
||||
if (empty($qText)) $qText = $line;
|
||||
}
|
||||
|
||||
$currentQuestion = [
|
||||
'question_text' => $qText,
|
||||
'question_type' => $qType,
|
||||
'is_required' => 1,
|
||||
'description' => '',
|
||||
'options' => $options,
|
||||
'scale_min' => $scaleMin,
|
||||
'scale_max' => $scaleMax
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if line contains options for the current question
|
||||
// e.g. "[ ] ชาย [ ] หญิง", "( ) 1. นักเรียน ( ) 2. ครู", "1) ปวช. 2) ปวส."
|
||||
if ($currentQuestion !== null) {
|
||||
$extractedOptions = extractOptionsFromLine($line);
|
||||
if (!empty($extractedOptions)) {
|
||||
if (!is_array($currentQuestion['options'])) {
|
||||
$currentQuestion['options'] = [];
|
||||
}
|
||||
foreach ($extractedOptions as $opt) {
|
||||
if (!in_array($opt, $currentQuestion['options'])) {
|
||||
$currentQuestion['options'][] = $opt;
|
||||
}
|
||||
}
|
||||
$currentQuestion['question_type'] = 'radio';
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is a continuation of question text
|
||||
if (empty($currentQuestion['options']) && $currentQuestion['question_type'] === 'radio' && $secCategory !== 'general') {
|
||||
// Could be multi-line question text
|
||||
if (mb_strlen($line, 'UTF-8') < 100 && !preg_match('/^[0-9]/', $line)) {
|
||||
$currentQuestion['question_text'] .= ' ' . $line;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add last question
|
||||
if ($currentQuestion !== null) {
|
||||
if ($currentHeading !== null) {
|
||||
$currentHeading['questions'][] = $currentQuestion;
|
||||
} else {
|
||||
$questions[] = $currentQuestion;
|
||||
}
|
||||
}
|
||||
|
||||
// Add last heading
|
||||
if ($currentHeading !== null) {
|
||||
$headings[] = $currentHeading;
|
||||
}
|
||||
|
||||
// If section has neither questions nor headings, check if raw lines can be converted to text questions
|
||||
if (empty($questions) && empty($headings) && !empty($rawSecLines)) {
|
||||
foreach ($rawSecLines as $raw) {
|
||||
if (mb_strlen($raw, 'UTF-8') > 3) {
|
||||
$questions[] = [
|
||||
'question_text' => $raw,
|
||||
'question_type' => ($secCategory === 'suggestion') ? 'text' : 'radio',
|
||||
'is_required' => 1,
|
||||
'description' => '',
|
||||
'options' => null,
|
||||
'scale_min' => 1,
|
||||
'scale_max' => 5
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$parsedSections[] = [
|
||||
'section_number' => $secCounter++,
|
||||
'title' => $sec['title'],
|
||||
'description' => $sec['description'],
|
||||
'category' => $secCategory,
|
||||
'headings' => $headings,
|
||||
'questions' => $questions
|
||||
];
|
||||
}
|
||||
|
||||
// Default safety fallback if empty
|
||||
if (empty($parsedSections)) {
|
||||
$parsedSections[] = [
|
||||
'section_number' => 1,
|
||||
'title' => 'ตอนที่ 1 ข้อมูลและข้อคำถาม',
|
||||
'description' => '',
|
||||
'category' => 'general',
|
||||
'headings' => [],
|
||||
'questions' => []
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => $title,
|
||||
'description' => $description ?: 'แบบประเมินความพึงพอใจ นำเข้าจากไฟล์ PDF',
|
||||
'sections' => $parsedSections
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract multiple choice options from a line
|
||||
*/
|
||||
function extractOptionsFromLine($line) {
|
||||
$options = [];
|
||||
|
||||
// Pattern 1: [ ] ชาย [ ] หญิง or ( ) 1. นักเรียน ( ) 2. ครู
|
||||
if (preg_match_all('/(?:\[\s*\]|\(\s*\)|□|○)\s*([^\(\)\[\]□○\t\n]+)/u', $line, $matches)) {
|
||||
foreach ($matches[1] as $opt) {
|
||||
$cleanOpt = trim(preg_replace('/^[0-9๑-๙]+[\.\)]\s*/u', '', trim($opt)));
|
||||
if (!empty($cleanOpt)) {
|
||||
$options[] = $cleanOpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pattern 2: 1) ชาย 2) หญิง 3) อื่นๆ
|
||||
if (empty($options) && preg_match_all('/(?:^|\s+)[0-9๑-๙]+[\)]\s*([^\d\)]+)/u', $line, $matches)) {
|
||||
foreach ($matches[1] as $opt) {
|
||||
$cleanOpt = trim($opt);
|
||||
if (!empty($cleanOpt) && mb_strlen($cleanOpt, 'UTF-8') < 60) {
|
||||
$options[] = $cleanOpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
Reference in New Issue
Block a user