'Missing survey_id'], 400); // Check access $db = getDB(); if (!isAdmin()) { $stmt = $db->prepare("SELECT user_id FROM surveys WHERE id = ?"); $stmt->execute([$surveyId]); $survey = $stmt->fetch(); if (!$survey || $survey['user_id'] != $_SESSION['user_id']) { jsonResponse(['error' => 'ไม่มีสิทธิ์'], 403); } } /** * 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; } switch ($action) { case 'summary': getSummary($surveyId); break; case 'detail': getDetail($surveyId); break; default: jsonResponse(['error' => 'Invalid action'], 400); } function getSummary($surveyId) { $db = getDB(); // Total responses $stmt = $db->prepare("SELECT COUNT(*) as total FROM survey_responses WHERE survey_id = ?"); $stmt->execute([$surveyId]); $total = $stmt->fetch()['total']; // Get survey info $stmt = $db->prepare("SELECT title, status, created_at FROM surveys WHERE id = ?"); $stmt->execute([$surveyId]); $surveyInfo = $stmt->fetch(); // Fetch all answers for rating questions in this survey and compute mean/sd $stmt = $db->prepare(" SELECT ra.answer_value FROM response_answers ra JOIN survey_questions sq ON ra.question_id = sq.id JOIN survey_sections ss ON sq.section_id = ss.id WHERE ss.survey_id = ? AND sq.question_type = 'radio' AND sq.options IS NULL "); $stmt->execute([$surveyId]); $rows = $stmt->fetchAll(PDO::FETCH_COLUMN); $scores = []; foreach ($rows as $ans) { $sc = parseRatingScore($ans); if ($sc !== null) { $scores[] = $sc; } } $overallMean = 0; $overallSd = 0; if (count($scores) > 0) { $overallMean = array_sum($scores) / count($scores); if (count($scores) > 1) { $sumSq = 0; foreach ($scores as $s) { $sumSq += pow($s - $overallMean, 2); } $overallSd = sqrt($sumSq / (count($scores) - 1)); } } jsonResponse([ 'survey' => $surveyInfo, 'total_responses' => (int)$total, 'overall_mean' => round($overallMean, 2), 'overall_sd' => round($overallSd, 2) ]); } function getDetail($surveyId) { $db = getDB(); // Total responses $stmt = $db->prepare("SELECT COUNT(*) as total FROM survey_responses WHERE survey_id = ?"); $stmt->execute([$surveyId]); $totalResponses = (int)$stmt->fetch()['total']; // Get sections with questions $stmt = $db->prepare("SELECT * FROM survey_sections WHERE survey_id = ? ORDER BY section_number"); $stmt->execute([$surveyId]); $sections = $stmt->fetchAll(); $result = []; foreach ($sections as $section) { $qStmt = $db->prepare("SELECT * FROM survey_questions WHERE section_id = ? ORDER BY order_num"); $qStmt->execute([$section['id']]); $questions = $qStmt->fetchAll(); $sectionStats = [ 'section_number' => $section['section_number'], 'title' => $section['title'], 'headings' => [], 'questions' => [] ]; // Load headings for this section $headingStmt = $db->prepare("SELECT * FROM survey_headings WHERE section_id = ? ORDER BY order_num"); $headingStmt->execute([$section['id']]); $headings = $headingStmt->fetchAll(); foreach ($questions as $q) { $qStat = [ 'id' => $q['id'], 'heading_id' => $q['heading_id'] ?? null, 'question_text' => $q['question_text'], 'question_type' => $q['question_type'] ]; if ($q['question_type'] === 'radio' && empty($q['options'])) { // Rating scale question (1-5) $aStmt = $db->prepare(" SELECT answer_value, COUNT(*) as cnt FROM response_answers WHERE question_id = ? AND answer_value != '' GROUP BY answer_value ORDER BY answer_value "); $aStmt->execute([$q['id']]); $freqData = $aStmt->fetchAll(); $frequencies = []; $values = []; $totalAnswers = 0; for ($i = 1; $i <= 5; $i++) { $frequencies[$i] = ['value' => $i, 'count' => 0, 'percentage' => 0]; } foreach ($freqData as $fd) { $rawVal = trim($fd['answer_value']); $cnt = (int)$fd['cnt']; $val = parseRatingScore($rawVal); if ($val >= 1 && $val <= 5) { $frequencies[$val]['count'] += $cnt; $totalAnswers += $cnt; for ($j = 0; $j < $cnt; $j++) { $values[] = $val; } } } // Calculate percentages foreach ($frequencies as &$f) { $f['percentage'] = $totalAnswers > 0 ? round(($f['count'] / $totalAnswers) * 100, 1) : 0; } unset($f); // Calculate mean $mean = count($values) > 0 ? array_sum($values) / count($values) : 0; // Calculate SD $sd = 0; if (count($values) > 1) { $sumSquaredDiff = 0; foreach ($values as $v) { $sumSquaredDiff += pow($v - $mean, 2); } $sd = sqrt($sumSquaredDiff / (count($values) - 1)); } // Interpretation $interpretation = ''; if ($mean >= 4.51) $interpretation = 'มากที่สุด'; elseif ($mean >= 3.51) $interpretation = 'มาก'; elseif ($mean >= 2.51) $interpretation = 'ปานกลาง'; elseif ($mean >= 1.51) $interpretation = 'น้อย'; else $interpretation = 'น้อยที่สุด'; $qStat['mean'] = round($mean, 2); $qStat['sd'] = round($sd, 2); $qStat['interpretation'] = $interpretation; $qStat['frequencies'] = array_values($frequencies); $qStat['total_answers'] = $totalAnswers; } elseif ($q['question_type'] === 'text') { // Collect text responses $aStmt = $db->prepare(" SELECT answer_value FROM response_answers WHERE question_id = ? AND answer_value != '' ORDER BY id DESC "); $aStmt->execute([$q['id']]); $qStat['text_responses'] = array_column($aStmt->fetchAll(), 'answer_value'); $qStat['total_answers'] = count($qStat['text_responses']); } elseif (in_array($q['question_type'], ['dropdown', 'checkbox', 'radio'])) { // Frequency for dropdown/checkbox/custom radio $aStmt = $db->prepare(" SELECT answer_value, COUNT(*) as cnt FROM response_answers WHERE question_id = ? AND answer_value != '' GROUP BY answer_value ORDER BY cnt DESC "); $aStmt->execute([$q['id']]); $freqData = $aStmt->fetchAll(); $totalAnswers = array_sum(array_column($freqData, 'cnt')); $qStat['frequencies'] = array_map(function($fd) use ($totalAnswers) { return [ 'value' => $fd['answer_value'], 'count' => (int)$fd['cnt'], 'percentage' => $totalAnswers > 0 ? round(((int)$fd['cnt'] / $totalAnswers) * 100, 1) : 0 ]; }, $freqData); $qStat['total_answers'] = $totalAnswers; } $sectionStats['questions'][] = $qStat; } // Build heading stats groups $headingStats = []; foreach ($headings as $h) { $hStat = [ 'id' => $h['id'], 'title' => $h['title'], 'description' => $h['description'], 'questions' => [] ]; foreach ($sectionStats['questions'] as $qs) { if (($qs['heading_id'] ?? null) == $h['id']) { $hStat['questions'][] = $qs; } } $headingStats[] = $hStat; } $sectionStats['headings'] = $headingStats; // Filter loose questions (no heading) $sectionStats['questions'] = array_values(array_filter($sectionStats['questions'], function($qs) { return empty($qs['heading_id']); })); $result[] = $sectionStats; } jsonResponse([ 'total_responses' => $totalResponses, 'sections' => $result ]); }