Initial commit - TPU Survey System with Google Forms & Certificate Generator

This commit is contained in:
pongpon pitisuk
2026-09-12 11:56:34 +07:00
commit 72eb7cd0c8
355 changed files with 117012 additions and 0 deletions
+247
View File
@@ -0,0 +1,247 @@
<?php
/**
* Statistics API
* GET ?action=summary&survey_id=X — Summary statistics
* GET ?action=detail&survey_id=X — Detailed per-question stats
*/
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/../config/auth.php';
header('Content-Type: application/json; charset=utf-8');
requireLogin();
$action = $_GET['action'] ?? 'summary';
$surveyId = (int)($_GET['survey_id'] ?? 0);
if (!$surveyId) jsonResponse(['error' => '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);
}
}
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();
// Get overall mean and SD for radio/rating questions
$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
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]+$'
");
$stmt->execute([$surveyId]);
$overall = $stmt->fetch();
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
]);
}
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'])) {
// Calculate mean, SD, frequency for rating questions (1-5)
$aStmt = $db->prepare("
SELECT answer_value, COUNT(*) as cnt
FROM response_answers
WHERE question_id = ? AND answer_value REGEXP '^[0-9]+$'
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) {
$val = (int)$fd['answer_value'];
if ($val >= 1 && $val <= 5) {
$frequencies[$val]['count'] = (int)$fd['cnt'];
$totalAnswers += (int)$fd['cnt'];
for ($j = 0; $j < (int)$fd['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 = ?
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
]);
}