made client data browsable with dropdown and version diffs

This commit is contained in:
2026-06-01 13:29:10 +02:00
parent bee7b74e53
commit 3d56abd124
10 changed files with 971 additions and 83 deletions

View File

@ -15,6 +15,7 @@ const QDB_DEV_GLASS_POINTS = [
];
require_once __DIR__ . '/app_answers.php';
require_once __DIR__ . '/submissions.php';
/**
* @return array{imported: array, skipped: array, errors: string[]}
@ -33,6 +34,7 @@ function qdb_import_dev_fixture(PDO $pdo, array $fixture): array
'coaches' => 0,
'clients' => 0,
'completions' => 0,
'submissions' => 0,
'answers' => 0,
];
$skipped = [
@ -165,21 +167,57 @@ function qdb_import_dev_fixture(PDO $pdo, array $fixture): array
continue;
}
$completedAt = isset($row['completedAt']) ? (int)$row['completedAt'] : ($now - ($variant % 90) * 86400);
$startedAt = isset($row['startedAt']) ? (int)$row['startedAt'] : ($completedAt - 600);
$answers = qdb_dev_build_answers_for_questionnaire($pdo, $qnID, $variant);
$answerCount = qdb_dev_persist_submission(
$pdo,
$qnID,
$clientCode,
(string)$coachID,
$answers,
$startedAt,
$completedAt
$coachUserStmt = $pdo->prepare(
"SELECT u.userID FROM users u
WHERE u.role = 'coach' AND u.entityID = :cid LIMIT 1"
);
$coachUserStmt->execute([':cid' => $coachID]);
$coachUserID = (string)($coachUserStmt->fetchColumn() ?: '');
$tokenRec = ['userID' => $coachUserID, 'role' => 'coach'];
$uploads = qdb_dev_normalize_uploads($row, $variant, $now);
$archiveSubmissions = qdb_table_exists($pdo, 'questionnaire_submission');
foreach ($uploads as $uploadIndex => $upload) {
$variantSeed = (int)($upload['variant'] ?? ($variant + $uploadIndex));
$completedAt = (int)($upload['submittedAt'] ?? $upload['completedAt'] ?? $now);
$startedAt = (int)($upload['startedAt'] ?? ($completedAt - 600));
$answers = qdb_dev_build_answers_for_questionnaire($pdo, $qnID, $variantSeed);
$answerCount = qdb_dev_persist_submission(
$pdo,
$qnID,
$clientCode,
(string)$coachID,
$answers,
$startedAt,
$completedAt
);
$imported['answers'] += $answerCount;
if ($archiveSubmissions) {
$spStmt = $pdo->prepare(
'SELECT sumPoints FROM completed_questionnaire
WHERE clientCode = :cc AND questionnaireID = :qn'
);
$spStmt->execute([':cc' => $clientCode, ':qn' => $qnID]);
$sumPoints = (int)($spStmt->fetchColumn() ?: 0);
qdb_record_submission_after_submit(
$pdo,
$clientCode,
$qnID,
$tokenRec,
$startedAt,
$completedAt,
$sumPoints,
(string)$coachID,
$completedAt
);
$imported['submissions']++;
}
}
$imported['completions']++;
$imported['answers'] += $answerCount;
$variant++;
}
@ -200,12 +238,56 @@ function qdb_dev_validate_fixture(array $fixture): string
if ($prefix === '' || !preg_match('/^dev[a-z0-9_]*$/i', $prefix)) {
json_error('INVALID_FIELD', 'fixture.prefix must start with "dev" (e.g. dev_)', 400);
}
if ((int)($fixture['fixtureVersion'] ?? 0) !== 1) {
json_error('INVALID_FIELD', 'fixtureVersion must be 1', 400);
$fixtureVersion = (int)($fixture['fixtureVersion'] ?? 0);
if ($fixtureVersion !== 1 && $fixtureVersion !== 2) {
json_error('INVALID_FIELD', 'fixtureVersion must be 1 or 2', 400);
}
return $prefix;
}
/**
* @return list<array{submittedAt: int, startedAt: int, variant: int}>
*/
function qdb_dev_normalize_uploads(array $row, int $fallbackVariant, int $now): array
{
if (!empty($row['uploads']) && is_array($row['uploads'])) {
$out = [];
foreach ($row['uploads'] as $i => $upload) {
if (!is_array($upload)) {
continue;
}
$submittedAt = (int)($upload['submittedAt'] ?? $upload['completedAt'] ?? $now);
$startedAt = (int)($upload['startedAt'] ?? ($submittedAt - 600));
$out[] = [
'submittedAt' => $submittedAt,
'startedAt' => $startedAt,
'variant' => (int)($upload['variant'] ?? ($fallbackVariant + $i)),
];
}
if ($out !== []) {
usort($out, static fn($a, $b) => $a['submittedAt'] <=> $b['submittedAt']);
return $out;
}
}
$versionCount = max(1, (int)($row['versions'] ?? $row['uploadCount'] ?? 1));
$finalCompleted = isset($row['completedAt']) ? (int)$row['completedAt'] : ($now - ($fallbackVariant % 90) * 86400);
$uploads = [];
for ($v = 0; $v < $versionCount; $v++) {
$gapDays = ($versionCount - 1 - $v) * 14 + ($fallbackVariant % 5);
$completedAt = $finalCompleted - ($gapDays * 86400);
$uploads[] = [
'submittedAt' => $completedAt,
'startedAt' => isset($row['startedAt']) && $v === $versionCount - 1
? (int)$row['startedAt']
: ($completedAt - 600 - ($v * 120)),
'variant' => $fallbackVariant + $v,
];
}
usort($uploads, static fn($a, $b) => $a['submittedAt'] <=> $b['submittedAt']);
return $uploads;
}
function qdb_dev_has_prefix(string $value, string $prefix): bool
{
return str_starts_with(strtolower($value), strtolower($prefix))
@ -603,6 +685,8 @@ function qdb_remove_dev_test_data(PDO $pdo, array $options = []): array
$clientLike = $clientPrefix . '%';
$deleted = [
'submissions' => 0,
'followup_notes' => 0,
'client_answers' => 0,
'completed_questionnaires' => 0,
'clients' => 0,
@ -673,17 +757,15 @@ function qdb_remove_dev_test_data(PDO $pdo, array $options = []): array
$pdo->beginTransaction();
try {
$deleted['client_answers'] = qdb_dev_delete_by_ids($pdo, 'client_answer', 'clientCode', $clientCodeList);
if ($clientCodeList !== []) {
foreach (array_chunk($clientCodeList, 200) as $chunk) {
$ph = implode(',', array_fill(0, count($chunk), '?'));
$delCq = $pdo->prepare("DELETE FROM completed_questionnaire WHERE clientCode IN ($ph)");
$delCq->execute($chunk);
$deleted['completed_questionnaires'] += $delCq->rowCount();
}
$responseDeleted = qdb_delete_client_response_data($pdo, $clientCodeList);
$deleted['submissions'] += $responseDeleted['submissions'];
$deleted['followup_notes'] += $responseDeleted['followup_notes'];
$deleted['client_answers'] += $responseDeleted['client_answers'];
$deleted['completed_questionnaires'] += $responseDeleted['completed_questionnaires'];
}
if ($coachIdList !== []) {
$deleted['submissions'] += qdb_delete_submissions_for_coaches($pdo, $coachIdList);
$deleted['completed_questionnaires'] += qdb_dev_delete_by_ids(
$pdo,
'completed_questionnaire',