You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.7 KiB
167 lines
4.7 KiB
import '../models/deck.dart';
|
|
import '../models/question.dart';
|
|
import '../models/deck_config.dart';
|
|
import '../algorithms/priority_manager.dart';
|
|
|
|
/// Service for managing deck operations.
|
|
class DeckService {
|
|
/// Marks a question as known (manual override).
|
|
///
|
|
/// Sets streak to threshold and isKnown to true.
|
|
static Deck markQuestionAsKnown({
|
|
required Deck deck,
|
|
required String questionId,
|
|
}) {
|
|
final updatedQuestions = deck.questions.map((q) {
|
|
if (q.id == questionId) {
|
|
return q.copyWith(
|
|
consecutiveCorrect: deck.config.requiredConsecutiveCorrect,
|
|
isKnown: true,
|
|
);
|
|
}
|
|
return q;
|
|
}).toList();
|
|
|
|
return deck.copyWith(questions: updatedQuestions);
|
|
}
|
|
|
|
/// Marks a question as needs practice (manual override).
|
|
///
|
|
/// Sets isKnown to false and streak to 0, but keeps priority unchanged.
|
|
static Deck markQuestionAsNeedsPractice({
|
|
required Deck deck,
|
|
required String questionId,
|
|
}) {
|
|
final updatedQuestions = deck.questions.map((q) {
|
|
if (q.id == questionId) {
|
|
return q.copyWith(
|
|
isKnown: false,
|
|
consecutiveCorrect: 0,
|
|
// priorityPoints remains unchanged
|
|
);
|
|
}
|
|
return q;
|
|
}).toList();
|
|
|
|
return deck.copyWith(questions: updatedQuestions);
|
|
}
|
|
|
|
/// Resets the entire deck.
|
|
///
|
|
/// Resets all questions: streak = 0, isKnown = false, priority = 0.
|
|
/// Optionally resets totalAttempts if [resetAttemptCounts] is true.
|
|
/// Optionally clears attempt history if [clearAttemptHistory] is true.
|
|
static Deck resetDeck({
|
|
required Deck deck,
|
|
bool resetAttemptCounts = false,
|
|
bool clearAttemptHistory = false,
|
|
}) {
|
|
final updatedQuestions = deck.questions.map((q) {
|
|
return q.copyWith(
|
|
consecutiveCorrect: 0,
|
|
isKnown: false,
|
|
priorityPoints: 0,
|
|
lastAttemptIndex: -1,
|
|
totalCorrectAttempts: resetAttemptCounts ? 0 : q.totalCorrectAttempts,
|
|
totalAttempts: resetAttemptCounts ? 0 : q.totalAttempts,
|
|
);
|
|
}).toList();
|
|
|
|
return deck.copyWith(
|
|
questions: updatedQuestions,
|
|
currentAttemptIndex: 0,
|
|
attemptHistory: clearAttemptHistory ? [] : deck.attemptHistory,
|
|
);
|
|
}
|
|
|
|
/// Updates a question's state after an answer.
|
|
///
|
|
/// Applies correctness rules and updates streak, priority, and isKnown.
|
|
static Question updateQuestionAfterAnswer({
|
|
required Question question,
|
|
required bool isCorrect,
|
|
required DeckConfig config,
|
|
required int currentAttemptIndex,
|
|
}) {
|
|
Question updated = question;
|
|
|
|
if (isCorrect) {
|
|
// Increment streak
|
|
final newStreak = question.consecutiveCorrect + 1;
|
|
updated = updated.copyWith(consecutiveCorrect: newStreak);
|
|
|
|
// Update isKnown if streak reaches threshold
|
|
if (newStreak >= config.requiredConsecutiveCorrect) {
|
|
updated = updated.copyWith(isKnown: true);
|
|
}
|
|
|
|
// Decrease priority
|
|
updated = PriorityManager.applyAnswerResult(
|
|
question: updated,
|
|
isCorrect: true,
|
|
config: config,
|
|
);
|
|
|
|
// Update totals
|
|
updated = updated.copyWith(
|
|
totalCorrectAttempts: question.totalCorrectAttempts + 1,
|
|
totalAttempts: question.totalAttempts + 1,
|
|
);
|
|
} else {
|
|
// Reset streak
|
|
updated = updated.copyWith(
|
|
consecutiveCorrect: 0,
|
|
isKnown: false,
|
|
);
|
|
|
|
// Increase priority
|
|
updated = PriorityManager.applyAnswerResult(
|
|
question: updated,
|
|
isCorrect: false,
|
|
config: config,
|
|
);
|
|
|
|
// Update totals
|
|
updated = updated.copyWith(
|
|
totalAttempts: question.totalAttempts + 1,
|
|
);
|
|
}
|
|
|
|
// Update lastAttemptIndex
|
|
updated = updated.copyWith(lastAttemptIndex: currentAttemptIndex);
|
|
|
|
return updated;
|
|
}
|
|
|
|
/// Updates a question with manual override after an answer.
|
|
///
|
|
/// If user marks as "Needs Practice" even after correct answer,
|
|
/// ignore correctness and don't increment streak or decrease priority.
|
|
static Question updateQuestionWithManualOverride({
|
|
required Question question,
|
|
required bool isCorrect,
|
|
required bool userMarkedNeedsPractice,
|
|
required DeckConfig config,
|
|
required int currentAttemptIndex,
|
|
}) {
|
|
if (userMarkedNeedsPractice) {
|
|
// User marked as needs practice - ignore correctness
|
|
// Don't increment streak, don't decrease priority
|
|
// Just update attempt counts and lastAttemptIndex
|
|
return question.copyWith(
|
|
totalAttempts: question.totalAttempts + 1,
|
|
lastAttemptIndex: currentAttemptIndex,
|
|
);
|
|
} else {
|
|
// Normal flow
|
|
return updateQuestionAfterAnswer(
|
|
question: question,
|
|
isCorrect: isCorrect,
|
|
config: config,
|
|
currentAttemptIndex: currentAttemptIndex,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|