prepare("SELECT * FROM questionnaire WHERE questionnaireID = :id"); $qn->execute([':id' => $qnID]); $questionnaire = $qn->fetch(PDO::FETCH_ASSOC); if (!$questionnaire) { qdb_discard($tmpDb, $lockFp); json_error('NOT_FOUND', 'Questionnaire not found', 404); } $qStmt = $pdo->prepare(" SELECT questionID, defaultText, type, orderIndex, isRequired, configJson FROM question WHERE questionnaireID = :id ORDER BY orderIndex "); $qStmt->execute([':id' => $qnID]); $questions = $qStmt->fetchAll(PDO::FETCH_ASSOC); $questionIDs = array_column($questions, 'questionID'); foreach ($questions as &$q) { $q['isRequired'] = (int)$q['isRequired']; $q['orderIndex'] = (int)$q['orderIndex']; $ao = $pdo->prepare("SELECT answerOptionID, defaultText, points, orderIndex FROM answer_option WHERE questionID = :qid ORDER BY orderIndex"); $ao->execute([':qid' => $q['questionID']]); $q['answerOptions'] = $ao->fetchAll(PDO::FETCH_ASSOC); foreach ($q['answerOptions'] as &$opt) { $opt['points'] = (int)$opt['points']; $opt['orderIndex'] = (int)$opt['orderIndex']; } } unset($q); [$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl'); $sql = " SELECT cl.clientCode, cl.coachID, co.username AS coachUsername, sv.username AS supervisorUsername, cq.status, cq.startedAt, cq.completedAt, cq.sumPoints, cq.assignedByCoach FROM client cl INNER JOIN completed_questionnaire cq ON cq.clientCode = cl.clientCode AND cq.questionnaireID = :qnid LEFT JOIN coach co ON co.coachID = cl.coachID LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID WHERE $rbacClause "; $params = array_merge([':qnid' => $qnID], $rbacParams); if ($clientCode) { $sql .= " AND cl.clientCode = :cc"; $params[':cc'] = $clientCode; } $sql .= " ORDER BY cl.clientCode"; $cStmt = $pdo->prepare($sql); $cStmt->execute($params); $clients = $cStmt->fetchAll(PDO::FETCH_ASSOC); if (!empty($questionIDs) && !empty($clients)) { $qPlaceholders = implode(',', array_fill(0, count($questionIDs), '?')); $answerStmt = $pdo->prepare(" SELECT clientCode, questionID, answerOptionID, freeTextValue, numericValue, answeredAt FROM client_answer WHERE clientCode = ? AND questionID IN ($qPlaceholders) "); foreach ($clients as &$c) { $c['sumPoints'] = $c['sumPoints'] !== null ? (int)$c['sumPoints'] : null; $c['startedAt'] = $c['startedAt'] !== null ? (int)$c['startedAt'] : null; $c['completedAt'] = $c['completedAt'] !== null ? (int)$c['completedAt'] : null; $bindParams = array_merge([$c['clientCode']], $questionIDs); $answerStmt->execute($bindParams); $answers = $answerStmt->fetchAll(PDO::FETCH_ASSOC); $answerMap = []; foreach ($answers as $a) { $answerMap[$a['questionID']] = [ 'answerOptionID' => $a['answerOptionID'], 'freeTextValue' => $a['freeTextValue'], 'numericValue' => $a['numericValue'] !== null ? (float)$a['numericValue'] : null, 'answeredAt' => $a['answeredAt'] !== null ? (int)$a['answeredAt'] : null, ]; } $c['answers'] = $answerMap; } unset($c); } else { foreach ($clients as &$c) { $c['sumPoints'] = $c['sumPoints'] !== null ? (int)$c['sumPoints'] : null; $c['startedAt'] = $c['startedAt'] !== null ? (int)$c['startedAt'] : null; $c['completedAt'] = $c['completedAt'] !== null ? (int)$c['completedAt'] : null; $c['answers'] = (object)[]; } unset($c); } qdb_discard($tmpDb, $lockFp); json_success([ "questionnaire" => $questionnaire, "questions" => $questions, "clients" => $clients, ]);