updated api
This commit is contained in:
178
handlers/client_export.php
Normal file
178
handlers/client_export.php
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../lib/activity.php';
|
||||
|
||||
$tokenRec = require_valid_token();
|
||||
require_role(['admin', 'supervisor'], $tokenRec);
|
||||
|
||||
if ($method !== 'GET') {
|
||||
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
|
||||
}
|
||||
|
||||
$clientCode = trim($_GET['clientCode'] ?? '');
|
||||
$exportAll = (strpos($route, 'export_all') !== false) || isset($_GET['export_all']);
|
||||
|
||||
[$pdo, $tmpDb, $lockFp] = qdb_open(false);
|
||||
[$rbacClause, $rbacParams] = rbac_client_filter($tokenRec, 'cl');
|
||||
|
||||
$clientFilter = '';
|
||||
$params = $rbacParams;
|
||||
if (!$exportAll) {
|
||||
if ($clientCode === '') {
|
||||
qdb_discard($tmpDb, $lockFp);
|
||||
json_error('MISSING_PARAM', 'clientCode query param required', 400);
|
||||
}
|
||||
$clientFilter = ' AND cl.clientCode = :cc';
|
||||
$params[':cc'] = $clientCode;
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT cl.clientCode, cl.coachID,
|
||||
co.username AS coachUsername,
|
||||
sv.username AS supervisorUsername
|
||||
FROM client cl
|
||||
LEFT JOIN coach co ON co.coachID = cl.coachID
|
||||
LEFT JOIN supervisor sv ON sv.supervisorID = co.supervisorID
|
||||
WHERE ($rbacClause) $clientFilter
|
||||
ORDER BY cl.clientCode
|
||||
";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$clients = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$exportAll && empty($clients)) {
|
||||
qdb_discard($tmpDb, $lockFp);
|
||||
json_error('NOT_FOUND', 'Client not found or not authorized', 404);
|
||||
}
|
||||
|
||||
$subStmt = $pdo->prepare("
|
||||
SELECT qs.submissionID, qs.clientCode, qs.questionnaireID, qs.submissionVersion,
|
||||
qs.status, qs.startedAt, qs.completedAt, qs.uploadedAt, qs.sumPoints,
|
||||
qs.submittedByRole, qn.name AS questionnaireName
|
||||
FROM questionnaire_submission qs
|
||||
JOIN questionnaire qn ON qn.questionnaireID = qs.questionnaireID
|
||||
WHERE qs.clientCode = :cc
|
||||
ORDER BY qs.uploadedAt, qs.submissionVersion
|
||||
");
|
||||
|
||||
$ansStmt = $pdo->prepare("
|
||||
SELECT sa.questionID, sa.answerOptionID, sa.freeTextValue, sa.numericValue,
|
||||
q.defaultText AS questionText, ao.defaultText AS optionText
|
||||
FROM submission_answer sa
|
||||
JOIN question q ON q.questionID = sa.questionID
|
||||
LEFT JOIN answer_option ao ON ao.answerOptionID = sa.answerOptionID
|
||||
WHERE sa.submissionID = :sid
|
||||
ORDER BY q.orderIndex
|
||||
");
|
||||
|
||||
$rows = [];
|
||||
foreach ($clients as $cl) {
|
||||
$cc = $cl['clientCode'];
|
||||
$subStmt->execute([':cc' => $cc]);
|
||||
foreach ($subStmt->fetchAll(PDO::FETCH_ASSOC) as $sub) {
|
||||
$ansStmt->execute([':sid' => $sub['submissionID']]);
|
||||
$answers = $ansStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (empty($answers)) {
|
||||
$rows[] = build_export_row($cl, $sub, null);
|
||||
continue;
|
||||
}
|
||||
foreach ($answers as $a) {
|
||||
$rows[] = build_export_row($cl, $sub, $a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qdb_discard($tmpDb, $lockFp);
|
||||
|
||||
try {
|
||||
[$pdoW, $tmpDbW, $lockFpW] = qdb_open(true);
|
||||
log_activity(
|
||||
$pdoW,
|
||||
$tokenRec,
|
||||
'client_data_exported',
|
||||
'client',
|
||||
$exportAll ? '*' : $clientCode,
|
||||
$exportAll ? 'Exported all client data' : "Exported data for $clientCode",
|
||||
['exportAll' => $exportAll, 'clientCode' => $exportAll ? null : $clientCode, 'rowCount' => count($rows)]
|
||||
);
|
||||
qdb_save($tmpDbW, $lockFpW);
|
||||
} catch (Throwable $e) {
|
||||
if (isset($tmpDbW, $lockFpW)) qdb_discard($tmpDbW, $lockFpW);
|
||||
error_log('Activity log failed on export: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$filename = $exportAll
|
||||
? 'clients_export_' . date('Y-m-d') . '.csv'
|
||||
: 'client_' . preg_replace('/[^a-zA-Z0-9_-]/', '_', $clientCode) . '_export.csv';
|
||||
|
||||
header('Content-Type: text/csv; charset=UTF-8');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
echo "\xEF\xBB\xBF";
|
||||
|
||||
$out = fopen('php://output', 'w');
|
||||
$headers = [
|
||||
'clientCode', 'coach', 'supervisor', 'questionnaireID', 'questionnaireName',
|
||||
'submissionVersion', 'submissionID', 'status', 'sumPoints',
|
||||
'startedAt', 'completedAt', 'uploadedAt', 'submittedByRole',
|
||||
'questionText', 'answerOption', 'freeTextValue', 'numericValue',
|
||||
];
|
||||
fputcsv($out, $headers);
|
||||
foreach ($rows as $r) {
|
||||
fputcsv($out, [
|
||||
$r['clientCode'],
|
||||
$r['coach'],
|
||||
$r['supervisor'],
|
||||
$r['questionnaireID'],
|
||||
$r['questionnaireName'],
|
||||
$r['submissionVersion'],
|
||||
$r['submissionID'],
|
||||
$r['status'],
|
||||
$r['sumPoints'],
|
||||
$r['startedAt'],
|
||||
$r['completedAt'],
|
||||
$r['uploadedAt'],
|
||||
$r['submittedByRole'],
|
||||
$r['questionText'],
|
||||
$r['answerOption'],
|
||||
$r['freeTextValue'],
|
||||
$r['numericValue'],
|
||||
]);
|
||||
}
|
||||
fclose($out);
|
||||
exit;
|
||||
|
||||
function build_export_row(array $cl, array $sub, ?array $a): array {
|
||||
$fmt = static function ($ms) {
|
||||
if ($ms === null || $ms === '') return '';
|
||||
return date('Y-m-d H:i', (int)($ms / 1000));
|
||||
};
|
||||
$answerVal = '';
|
||||
if ($a) {
|
||||
if (!empty($a['optionText'])) {
|
||||
$answerVal = $a['optionText'];
|
||||
} elseif ($a['freeTextValue'] !== null && $a['freeTextValue'] !== '') {
|
||||
$answerVal = $a['freeTextValue'];
|
||||
} elseif ($a['numericValue'] !== null) {
|
||||
$answerVal = (string)$a['numericValue'];
|
||||
}
|
||||
}
|
||||
return [
|
||||
'clientCode' => $cl['clientCode'],
|
||||
'coach' => $cl['coachUsername'] ?? $cl['coachID'] ?? '',
|
||||
'supervisor' => $cl['supervisorUsername'] ?? '',
|
||||
'questionnaireID' => $sub['questionnaireID'],
|
||||
'questionnaireName' => $sub['questionnaireName'] ?? '',
|
||||
'submissionVersion' => (int)$sub['submissionVersion'],
|
||||
'submissionID' => $sub['submissionID'],
|
||||
'status' => $sub['status'],
|
||||
'sumPoints' => $sub['sumPoints'] !== null ? (int)$sub['sumPoints'] : '',
|
||||
'startedAt' => $fmt($sub['startedAt']),
|
||||
'completedAt' => $fmt($sub['completedAt']),
|
||||
'uploadedAt' => $fmt($sub['uploadedAt']),
|
||||
'submittedByRole' => $sub['submittedByRole'] ?? '',
|
||||
'questionText' => $a['questionText'] ?? '',
|
||||
'answerOption' => $answerVal,
|
||||
'freeTextValue' => $a['freeTextValue'] ?? '',
|
||||
'numericValue' => $a['numericValue'] !== null ? $a['numericValue'] : '',
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user