93 Zeilen
3,5 KiB
PHP
93 Zeilen
3,5 KiB
PHP
|
|
<?php
|
||
|
|
require_once __DIR__ . '/sm2.php';
|
||
|
|
$user_id = require_auth();
|
||
|
|
|
||
|
|
// GET /api/cards/deck/{slug}
|
||
|
|
if (get_method() === 'GET' && ($segments[1] ?? '') === 'deck' && !empty($segments[2])) {
|
||
|
|
$slug = $segments[2];
|
||
|
|
$stmt = $pdo->prepare("
|
||
|
|
SELECT c.id, c.question, c.answer, c.level, c.sort_order,
|
||
|
|
cp.next_review, cp.last_rating, cp.last_reviewed_at,
|
||
|
|
cp.ease_factor, cp.interval_days, cp.repetitions,
|
||
|
|
d.name AS deck_name, d.slug AS deck_slug
|
||
|
|
FROM cards c
|
||
|
|
JOIN decks d ON d.id = c.deck_id AND d.slug = :slug
|
||
|
|
LEFT JOIN card_progress cp ON cp.card_id = c.id AND cp.user_id = :uid
|
||
|
|
ORDER BY c.sort_order
|
||
|
|
");
|
||
|
|
$stmt->execute([':slug' => $slug, ':uid' => $user_id]);
|
||
|
|
json_ok(['cards' => $stmt->fetchAll()]);
|
||
|
|
}
|
||
|
|
|
||
|
|
// GET /api/cards/due?deck={slug}&limit=N
|
||
|
|
if (get_method() === 'GET' && ($segments[1] ?? '') === 'due') {
|
||
|
|
$slug = get_param('deck', '');
|
||
|
|
$limit = max(1, min(50, (int) get_param('limit', 20)));
|
||
|
|
|
||
|
|
$query = "
|
||
|
|
SELECT c.id, c.question, c.answer, c.level, c.sort_order, c.deck_id,
|
||
|
|
d.slug AS deck_slug, d.name AS deck_name,
|
||
|
|
cp.next_review, cp.last_rating, cp.last_reviewed_at,
|
||
|
|
cp.ease_factor, cp.interval_days, cp.repetitions
|
||
|
|
FROM cards c
|
||
|
|
JOIN decks d ON d.id = c.deck_id
|
||
|
|
LEFT JOIN card_progress cp ON cp.card_id = c.id AND cp.user_id = :uid
|
||
|
|
WHERE (cp.next_review IS NULL OR cp.next_review <= CURRENT_DATE)
|
||
|
|
";
|
||
|
|
$params = [':uid' => $user_id];
|
||
|
|
|
||
|
|
if ($slug) {
|
||
|
|
$query .= " AND d.slug = :slug";
|
||
|
|
$params[':slug'] = $slug;
|
||
|
|
}
|
||
|
|
|
||
|
|
$query .= " ORDER BY cp.next_review ASC NULLS FIRST, c.sort_order LIMIT " . $limit;
|
||
|
|
|
||
|
|
$stmt = $pdo->prepare($query);
|
||
|
|
$stmt->execute($params);
|
||
|
|
json_ok(['cards' => $stmt->fetchAll()]);
|
||
|
|
}
|
||
|
|
|
||
|
|
// POST /api/cards/{id}/review
|
||
|
|
if (get_method() === 'POST' && is_numeric($segments[1] ?? '') && ($segments[2] ?? '') === 'review') {
|
||
|
|
$card_id = (int) $segments[1];
|
||
|
|
$body = get_json_body();
|
||
|
|
$rating = $body['rating'] ?? '';
|
||
|
|
|
||
|
|
if (!in_array($rating, ['easy', 'medium', 'hard', 'wrong'])) {
|
||
|
|
json_error('Ungueltige Bewertung. Erlaubt: easy, medium, hard, wrong');
|
||
|
|
}
|
||
|
|
|
||
|
|
$stmt = $pdo->prepare("SELECT id FROM cards WHERE id = :id");
|
||
|
|
$stmt->execute([':id' => $card_id]);
|
||
|
|
if (!$stmt->fetch()) json_error('Karte nicht gefunden', 404);
|
||
|
|
|
||
|
|
$stmt = $pdo->prepare("SELECT * FROM card_progress WHERE user_id = :uid AND card_id = :cid");
|
||
|
|
$stmt->execute([':uid' => $user_id, ':cid' => $card_id]);
|
||
|
|
$progress = $stmt->fetch();
|
||
|
|
|
||
|
|
$ease = $progress['ease_factor'] ?? 2.5;
|
||
|
|
$interval = $progress['interval_days'] ?? 0;
|
||
|
|
$reps = $progress['repetitions'] ?? 0;
|
||
|
|
|
||
|
|
$result = sm2_calculate($rating, $ease, $interval, $reps);
|
||
|
|
|
||
|
|
$stmt = $pdo->prepare("
|
||
|
|
INSERT INTO card_progress (user_id, card_id, ease_factor, interval_days, repetitions, next_review, last_rating, last_reviewed_at)
|
||
|
|
VALUES (:uid, :cid, :ease, :intv, :reps, :next, :rating, NOW())
|
||
|
|
ON CONFLICT (user_id, card_id) DO UPDATE SET
|
||
|
|
ease_factor = :ease, interval_days = :intv, repetitions = :reps,
|
||
|
|
next_review = :next, last_rating = :rating, last_reviewed_at = NOW()
|
||
|
|
");
|
||
|
|
$stmt->execute([
|
||
|
|
':uid' => $user_id, ':cid' => $card_id,
|
||
|
|
':ease' => $result['ease_factor'], ':intv' => $result['interval_days'],
|
||
|
|
':reps' => $result['repetitions'], ':next' => $result['next_review'],
|
||
|
|
':rating' => $rating
|
||
|
|
]);
|
||
|
|
|
||
|
|
json_ok(['progress' => $result]);
|
||
|
|
}
|
||
|
|
|
||
|
|
json_error('Unbekannter Cards-Endpunkt', 404);
|