115 lines
2.9 KiB
Dart
115 lines
2.9 KiB
Dart
import 'package:postgres/postgres.dart';
|
|
|
|
import 'spelling_progress_policy.dart';
|
|
|
|
/// Per-user familiarity with shared [spelling_items].
|
|
class UserSpellingProgressDb {
|
|
UserSpellingProgressDb(this._connection);
|
|
|
|
final Connection _connection;
|
|
|
|
/// Ensures a per-user link to [spellingItemId] with scores starting at zero.
|
|
Future<void> ensureLink({
|
|
required String firebaseUid,
|
|
required String spellingItemId,
|
|
}) async {
|
|
await _connection.execute(
|
|
Sql.named(
|
|
'''
|
|
INSERT INTO user_spelling_progress (
|
|
firebase_uid,
|
|
spelling_item_id,
|
|
familiarity_score,
|
|
attempt_count,
|
|
correct_count
|
|
) VALUES (
|
|
@uid,
|
|
@item_id::uuid,
|
|
0,
|
|
0,
|
|
0
|
|
)
|
|
ON CONFLICT (firebase_uid, spelling_item_id) DO NOTHING
|
|
''',
|
|
),
|
|
parameters: <String, dynamic>{
|
|
'uid': firebaseUid,
|
|
'item_id': spellingItemId,
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<void> recordQuizOutcome({
|
|
required String firebaseUid,
|
|
required String spellingItemId,
|
|
required bool correct,
|
|
}) async {
|
|
final DateTime now = DateTime.now().toUtc();
|
|
final String outcome = correct ? 'correct' : 'incorrect';
|
|
final int delta = correct
|
|
? SpellingProgressPolicy.correctDelta
|
|
: -SpellingProgressPolicy.incorrectDelta;
|
|
|
|
await _connection.execute(
|
|
Sql.named(
|
|
'''
|
|
INSERT INTO user_spelling_progress (
|
|
firebase_uid,
|
|
spelling_item_id,
|
|
familiarity_score,
|
|
attempt_count,
|
|
correct_count,
|
|
last_attempted_at,
|
|
last_outcome
|
|
) VALUES (
|
|
@uid,
|
|
@item_id::uuid,
|
|
@delta,
|
|
1,
|
|
@correct_inc,
|
|
@now,
|
|
@outcome
|
|
)
|
|
ON CONFLICT (firebase_uid, spelling_item_id) DO UPDATE SET
|
|
familiarity_score = user_spelling_progress.familiarity_score + @delta,
|
|
attempt_count = user_spelling_progress.attempt_count + 1,
|
|
correct_count = user_spelling_progress.correct_count + @correct_inc,
|
|
last_attempted_at = @now,
|
|
last_outcome = @outcome
|
|
''',
|
|
),
|
|
parameters: <String, dynamic>{
|
|
'uid': firebaseUid,
|
|
'item_id': spellingItemId,
|
|
'delta': delta,
|
|
'correct_inc': correct ? 1 : 0,
|
|
'now': now,
|
|
'outcome': outcome,
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<int?> familiarityScore({
|
|
required String firebaseUid,
|
|
required String spellingItemId,
|
|
}) async {
|
|
final Result result = await _connection.execute(
|
|
Sql.named(
|
|
'''
|
|
SELECT familiarity_score
|
|
FROM user_spelling_progress
|
|
WHERE firebase_uid = @uid AND spelling_item_id = @item_id::uuid
|
|
''',
|
|
),
|
|
parameters: <String, dynamic>{
|
|
'uid': firebaseUid,
|
|
'item_id': spellingItemId,
|
|
},
|
|
);
|
|
if (result.isEmpty) {
|
|
return null;
|
|
}
|
|
return (result.first[0]! as num).toInt();
|
|
}
|
|
}
|