Fix statistics calculation for imported Likert scale responses and support text rating answers
This commit is contained in:
+76
-15
@@ -28,6 +28,44 @@ if (!isAdmin()) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -52,24 +90,44 @@ function getSummary($surveyId) {
|
||||
$stmt->execute([$surveyId]);
|
||||
$surveyInfo = $stmt->fetch();
|
||||
|
||||
// Get overall mean and SD for radio/rating questions
|
||||
// Fetch all answers for rating questions in this survey and compute mean/sd
|
||||
$stmt = $db->prepare("
|
||||
SELECT
|
||||
AVG(CAST(ra.answer_value AS DECIMAL(10,2))) as overall_mean,
|
||||
STDDEV(CAST(ra.answer_value AS DECIMAL(10,2))) as overall_sd
|
||||
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 AND ra.answer_value REGEXP '^[0-9]+$'
|
||||
WHERE ss.survey_id = ? AND sq.question_type = 'radio' AND sq.options IS NULL
|
||||
");
|
||||
$stmt->execute([$surveyId]);
|
||||
$overall = $stmt->fetch();
|
||||
$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' => $overall['overall_mean'] ? round((float)$overall['overall_mean'], 2) : 0,
|
||||
'overall_sd' => $overall['overall_sd'] ? round((float)$overall['overall_sd'], 2) : 0
|
||||
'overall_mean' => round($overallMean, 2),
|
||||
'overall_sd' => round($overallSd, 2)
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -114,11 +172,11 @@ function getDetail($surveyId) {
|
||||
];
|
||||
|
||||
if ($q['question_type'] === 'radio' && empty($q['options'])) {
|
||||
// Calculate mean, SD, frequency for rating questions (1-5)
|
||||
// Rating scale question (1-5)
|
||||
$aStmt = $db->prepare("
|
||||
SELECT answer_value, COUNT(*) as cnt
|
||||
FROM response_answers
|
||||
WHERE question_id = ? AND answer_value REGEXP '^[0-9]+$'
|
||||
WHERE question_id = ? AND answer_value != ''
|
||||
GROUP BY answer_value
|
||||
ORDER BY answer_value
|
||||
");
|
||||
@@ -134,11 +192,14 @@ function getDetail($surveyId) {
|
||||
}
|
||||
|
||||
foreach ($freqData as $fd) {
|
||||
$val = (int)$fd['answer_value'];
|
||||
$rawVal = trim($fd['answer_value']);
|
||||
$cnt = (int)$fd['cnt'];
|
||||
$val = parseRatingScore($rawVal);
|
||||
|
||||
if ($val >= 1 && $val <= 5) {
|
||||
$frequencies[$val]['count'] = (int)$fd['cnt'];
|
||||
$totalAnswers += (int)$fd['cnt'];
|
||||
for ($j = 0; $j < (int)$fd['cnt']; $j++) {
|
||||
$frequencies[$val]['count'] += $cnt;
|
||||
$totalAnswers += $cnt;
|
||||
for ($j = 0; $j < $cnt; $j++) {
|
||||
$values[] = $val;
|
||||
}
|
||||
}
|
||||
@@ -193,7 +254,7 @@ function getDetail($surveyId) {
|
||||
$aStmt = $db->prepare("
|
||||
SELECT answer_value, COUNT(*) as cnt
|
||||
FROM response_answers
|
||||
WHERE question_id = ?
|
||||
WHERE question_id = ? AND answer_value != ''
|
||||
GROUP BY answer_value
|
||||
ORDER BY cnt DESC
|
||||
");
|
||||
|
||||
Reference in New Issue
Block a user