added categoryKey to questionnaire schema and updated related functions for questionnaire handling; enhanced API to fetch client answers

This commit is contained in:
2026-05-27 19:38:49 +02:00
parent 9c887537e8
commit 36b6d63266
9 changed files with 424 additions and 17 deletions

View File

@ -5,6 +5,7 @@
* GET ?id=<questionnaireID> -> single questionnaire in app JSON format
* GET ?translations=1 -> global app UI strings only (not questionnaire content); **no auth** (login screen).
* GET ?clients=1 -> list of clients assigned to the authenticated coach
* GET ?clientCode=X&answers=1 -> all completed questionnaire answers for one client (encrypted)
* POST -> submit interview answers for a client (coach / supervisor / admin).
* Re-posting the same clientCode + questionnaireID updates answers in place (re-edit).
* Sensitive POST/GET ?clients=1 bodies use encrypted payloads (HKDF from Bearer token).
@ -69,12 +70,15 @@ if ($method === 'POST') {
);
$qRows->execute([':qn' => $qnID]);
$shortIdMap = []; // shortId -> fullQuestionID
$shortIdToType = []; // shortId -> layout type
$freeTextMaxLen = []; // fullQuestionID -> maxLength
$symptomParentMap = qdb_glass_symptom_parent_map($pdo, $qnID);
foreach ($qRows->fetchAll(PDO::FETCH_ASSOC) as $qRow) {
$fullId = $qRow['questionID'];
$parts = explode('__', $fullId);
$shortIdMap[end($parts)] = $fullId;
$short = end($parts);
$shortIdMap[$short] = $fullId;
$shortIdToType[$short] = $qRow['type'] ?? '';
if (($qRow['type'] ?? '') === 'free_text') {
$cfg = json_decode($qRow['configJson'] ?? '{}', true) ?: [];
$freeTextMaxLen[$fullId] = max(1, min((int)($cfg['maxLength'] ?? 500), 10000));
@ -111,6 +115,9 @@ if ($method === 'POST') {
answeredAt = excluded.answeredAt
");
require_once __DIR__ . '/../lib/app_answers.php';
$answers = qdb_group_app_submit_answers($answers, $shortIdToType, $symptomParentMap);
foreach ($answers as $a) {
$shortId = trim($a['questionID'] ?? '');
if ($shortId === '') continue;
@ -149,6 +156,13 @@ if ($method === 'POST') {
$opt = $optionMap[$fullQID][(string)$optionKey];
$answerOptionID = $opt['answerOptionID'];
$sumPoints += $opt['points'];
} elseif (($shortIdToType[$shortId] ?? '') === 'multi_check_box_question'
&& $freeTextValue !== null && $freeTextValue !== '') {
foreach (qdb_decode_multi_check_values($freeTextValue) as $mk) {
if (isset($optionMap[$fullQID][$mk])) {
$sumPoints += $optionMap[$fullQID][$mk]['points'];
}
}
}
$answerInsert->execute([
@ -218,6 +232,34 @@ if ($method !== 'GET') {
$qnID = $_GET['id'] ?? '';
$translations = $_GET['translations'] ?? '';
$fetchClients = $_GET['clients'] ?? '';
$fetchAnswers = $_GET['answers'] ?? '';
$clientCode = trim($_GET['clientCode'] ?? '');
if ($fetchAnswers) {
require_role(['admin', 'supervisor', 'coach'], $tokenRec);
if ($clientCode === '') {
qdb_discard($tmpDb, $lockFp);
json_error('MISSING_PARAM', 'clientCode query param required', 400);
}
[$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');
$clStmt = $pdo->prepare(
"SELECT cl.clientCode FROM client cl WHERE cl.clientCode = :cc AND ($rbacClause)"
);
$clStmt->execute(array_merge([':cc' => $clientCode], $rbacParams));
if (!$clStmt->fetch()) {
qdb_discard($tmpDb, $lockFp);
json_error('NOT_FOUND', 'Client not found or not authorized', 404);
}
require_once __DIR__ . '/../lib/app_answers.php';
$questionnaires = qdb_export_app_client_answers_bundle($pdo, $clientCode);
qdb_discard($tmpDb, $lockFp);
json_success_sensitive([
'clientCode' => $clientCode,
'questionnaires' => $questionnaires,
], $tokenRec['_token']);
}
if ($fetchClients) {
require_role(['coach'], $tokenRec);
@ -364,7 +406,7 @@ if ($qnID) {
// Default: ordered questionnaire list
$rows = $pdo->query("
SELECT questionnaireID, name, showPoints, conditionJson
SELECT questionnaireID, name, showPoints, conditionJson, categoryKey
FROM questionnaire
WHERE state = 'active'
ORDER BY orderIndex
@ -372,12 +414,17 @@ $rows = $pdo->query("
$list = [];
foreach ($rows as $r) {
$list[] = [
$item = [
'id' => $r['questionnaireID'],
'name' => $r['name'],
'showPoints' => (bool)(int)$r['showPoints'],
'condition' => json_decode($r['conditionJson'], true) ?: new stdClass(),
];
$cat = trim($r['categoryKey'] ?? '');
if ($cat !== '') {
$item['categoryKey'] = $cat;
}
$list[] = $item;
}
qdb_discard($tmpDb, $lockFp);