231 lines
8.1 KiB
PHP
231 lines
8.1 KiB
PHP
<?php
|
|
/**
|
|
* Import Responses API (from CSV / Excel exported from Google Forms)
|
|
* POST ?survey_id=X
|
|
*/
|
|
|
|
require_once __DIR__ . '/../config/database.php';
|
|
require_once __DIR__ . '/../config/auth.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
requireLogin();
|
|
|
|
$surveyId = (int)($_GET['survey_id'] ?? $_POST['survey_id'] ?? 0);
|
|
|
|
if (!$surveyId) {
|
|
jsonResponse(['error' => 'Missing survey_id'], 400);
|
|
}
|
|
|
|
$db = getDB();
|
|
|
|
// Verify access
|
|
if (!isAdmin()) {
|
|
$stmt = $db->prepare("SELECT user_id FROM surveys WHERE id = ?");
|
|
$stmt->execute([$surveyId]);
|
|
$s = $stmt->fetch();
|
|
if (!$s || $s['user_id'] != $_SESSION['user_id']) {
|
|
jsonResponse(['error' => 'ไม่มีสิทธิ์เข้าถึงแบบสอบถามนี้'], 403);
|
|
}
|
|
}
|
|
|
|
// Check uploaded file
|
|
if (!isset($_FILES['csv_file']) || $_FILES['csv_file']['error'] !== UPLOAD_ERR_OK) {
|
|
jsonResponse(['error' => 'กรุณาอัปโหลดไฟล์ CSV คำตอบจาก Google Forms'], 400);
|
|
}
|
|
|
|
$filePath = $_FILES['csv_file']['tmp_name'];
|
|
|
|
// Read CSV
|
|
$handle = fopen($filePath, 'r');
|
|
if (!$handle) {
|
|
jsonResponse(['error' => 'ไม่สามารถเปิดไฟล์ CSV ได้'], 500);
|
|
}
|
|
|
|
// Check UTF-8 BOM
|
|
$bom = fread($handle, 3);
|
|
if ($bom !== "\xEF\xBB\xBF") {
|
|
rewind($handle);
|
|
}
|
|
|
|
// Get Headers (Row 1)
|
|
$headers = fgetcsv($handle, 0, ",");
|
|
if (!$headers) {
|
|
fclose($handle);
|
|
jsonResponse(['error' => 'ไฟล์ CSV ไม่มีข้อมูลหรือรูปแบบไม่ถูกต้อง'], 400);
|
|
}
|
|
|
|
// Clean headers
|
|
$cleanHeaders = array_map(function($h) {
|
|
return trim(preg_replace('/\s+/', ' ', $h));
|
|
}, $headers);
|
|
|
|
// Fetch all questions for this survey
|
|
$stmt = $db->prepare("
|
|
SELECT q.id, q.question_text, q.question_type, q.options
|
|
FROM survey_questions q
|
|
JOIN survey_sections s ON q.section_id = s.id
|
|
WHERE s.survey_id = ?
|
|
ORDER BY s.section_number, q.order_num
|
|
");
|
|
$stmt->execute([$surveyId]);
|
|
$questions = $stmt->fetchAll();
|
|
|
|
if (empty($questions)) {
|
|
fclose($handle);
|
|
jsonResponse(['error' => 'ไม่พบข้อคำถามในแบบสอบถามนี้'], 400);
|
|
}
|
|
|
|
// Helper: normalize text for comparison
|
|
function normalizeText($str) {
|
|
$str = mb_strtolower($str, 'UTF-8');
|
|
// Remove leading numbers or prefix like "1. ", "ข้อ 1. "
|
|
$str = preg_replace('/^[\d๑-๙\.]*[\.\)\s\-:\x{00A0}]*/u', '', $str);
|
|
return trim($str);
|
|
}
|
|
|
|
// Helper to parse Likert rating values from numbers or Thai text
|
|
function parseRatingScore($str) {
|
|
$trimmed = trim((string)$str);
|
|
if ($trimmed === '') return null;
|
|
|
|
if (is_numeric($trimmed)) {
|
|
$num = (int)$trimmed;
|
|
return ($num >= 1 && $num <= 5) ? $num : null;
|
|
}
|
|
|
|
if (preg_match('/^([1-5])\s*[\.\:\-\(\s]/u', $trimmed, $m)) {
|
|
return (int)$m[1];
|
|
}
|
|
|
|
if (mb_strpos($trimmed, 'มากที่สุด') !== false || mb_strpos($trimmed, 'ดีมาก') !== false || mb_strpos($trimmed, 'ยอดเยี่ยม') !== false || mb_strpos($trimmed, 'ดีเยี่ยม') !== false || mb_strpos($trimmed, 'เห็นด้วยอย่างยิ่ง') !== false) {
|
|
return 5;
|
|
}
|
|
if (mb_strpos($trimmed, 'น้อยที่สุด') !== false || mb_strpos($trimmed, 'ไม่เห็นด้วยอย่างยิ่ง') !== false) {
|
|
return 1;
|
|
}
|
|
if (mb_strpos($trimmed, 'มาก') !== false || mb_strpos($trimmed, 'ดี') !== false || mb_strpos($trimmed, 'เห็นด้วย') !== false) {
|
|
return 4;
|
|
}
|
|
if (mb_strpos($trimmed, 'ปานกลาง') !== false || mb_strpos($trimmed, 'พอใช้') !== false || mb_strpos($trimmed, 'ไม่แน่ใจ') !== false) {
|
|
return 3;
|
|
}
|
|
if (mb_strpos($trimmed, 'น้อย') !== false || mb_strpos($trimmed, 'ควรปรับปรุง') !== false || mb_strpos($trimmed, 'ต้องปรับปรุง') !== false || mb_strpos($trimmed, 'ไม่เห็นด้วย') !== false) {
|
|
return 2;
|
|
}
|
|
if (mb_strpos($trimmed, 'ปรับปรุง') !== false) {
|
|
return 1;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Map CSV Column Index -> question_id
|
|
$colToQuestionMap = [];
|
|
$questionsById = [];
|
|
foreach ($questions as $q) {
|
|
$questionsById[$q['id']] = $q;
|
|
}
|
|
|
|
foreach ($cleanHeaders as $colIdx => $headerText) {
|
|
if (empty($headerText) || strtolower($headerText) === 'timestamp' || strtolower($headerText) === 'ประทับเวลา') {
|
|
continue;
|
|
}
|
|
|
|
$normHeader = normalizeText($headerText);
|
|
|
|
// Extract inside bracket if Google Forms grid header e.g. "Heading [Question]"
|
|
$bracketText = '';
|
|
if (preg_match('/\[(.*?)\]/', $headerText, $m)) {
|
|
$bracketText = normalizeText($m[1]);
|
|
}
|
|
|
|
// Try exact or normalized matching against survey questions
|
|
foreach ($questions as $q) {
|
|
$normQText = normalizeText($q['question_text']);
|
|
|
|
if (
|
|
$normHeader === $normQText ||
|
|
($bracketText !== '' && ($bracketText === $normQText || strpos($bracketText, $normQText) !== false || strpos($normQText, $bracketText) !== false)) ||
|
|
strpos($normHeader, $normQText) !== false ||
|
|
strpos($normQText, $normHeader) !== false
|
|
) {
|
|
$colToQuestionMap[$colIdx] = $q['id'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback: If column mapping count is low, map columns sequentially to questions
|
|
if (count($colToQuestionMap) < count($questions) / 2) {
|
|
$qIdx = 0;
|
|
foreach ($cleanHeaders as $colIdx => $headerText) {
|
|
if (strtolower($headerText) === 'timestamp' || strtolower($headerText) === 'ประทับเวลา') continue;
|
|
if (isset($questions[$qIdx])) {
|
|
$colToQuestionMap[$colIdx] = $questions[$qIdx]['id'];
|
|
$qIdx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process Rows
|
|
$importedCount = 0;
|
|
|
|
$db->beginTransaction();
|
|
try {
|
|
while (($row = fgetcsv($handle, 0, ",")) !== false) {
|
|
if (empty(array_filter($row))) continue; // Skip empty lines
|
|
|
|
// Extract timestamp or use now
|
|
$submittedAt = date('Y-m-d H:i:s');
|
|
if (!empty($row[0])) {
|
|
$ts = strtotime($row[0]);
|
|
if ($ts !== false) {
|
|
$submittedAt = date('Y-m-d H:i:s', $ts);
|
|
}
|
|
}
|
|
|
|
// Insert into survey_responses
|
|
$stmtResp = $db->prepare("INSERT INTO survey_responses (survey_id, respondent_info, submitted_at) VALUES (?, ?, ?)");
|
|
$stmtResp->execute([
|
|
$surveyId,
|
|
json_encode(['imported' => true, 'source' => 'google_forms_csv'], JSON_UNESCAPED_UNICODE),
|
|
$submittedAt
|
|
]);
|
|
$responseId = $db->lastInsertId();
|
|
|
|
// Insert answers
|
|
foreach ($colToQuestionMap as $colIdx => $qId) {
|
|
$val = isset($row[$colIdx]) ? trim($row[$colIdx]) : '';
|
|
if ($val !== '') {
|
|
$targetQ = $questionsById[$qId] ?? null;
|
|
// Normalize rating score if it's a 5-point rating scale question
|
|
if ($targetQ && $targetQ['question_type'] === 'radio' && empty($targetQ['options'])) {
|
|
$score = parseRatingScore($val);
|
|
if ($score !== null) {
|
|
$val = (string)$score;
|
|
}
|
|
}
|
|
|
|
$stmtAns = $db->prepare("INSERT INTO response_answers (response_id, question_id, answer_value) VALUES (?, ?, ?)");
|
|
$stmtAns->execute([$responseId, $qId, $val]);
|
|
}
|
|
}
|
|
|
|
$importedCount++;
|
|
}
|
|
|
|
$db->commit();
|
|
fclose($handle);
|
|
|
|
jsonResponse([
|
|
'success' => true,
|
|
'message' => "นำเข้าข้อมูลคำตอบสำเร็จจำนวน $importedCount รายการ",
|
|
'imported_count' => $importedCount,
|
|
'mapped_questions' => count($colToQuestionMap)
|
|
]);
|
|
} catch (Exception $e) {
|
|
$db->rollBack();
|
|
fclose($handle);
|
|
jsonResponse(['error' => 'เกิดข้อผิดพลาดในการบันทึกคำตอบ: ' . $e->getMessage()], 500);
|
|
}
|