58 lines
1.9 KiB
Dart
58 lines
1.9 KiB
Dart
import 'courses/courses.dart';
|
|
import 'models.dart';
|
|
import 'speech_compare.dart';
|
|
|
|
class ReviewCheckResult {
|
|
const ReviewCheckResult({
|
|
required this.complete,
|
|
required this.message,
|
|
this.dictation,
|
|
});
|
|
|
|
final bool complete;
|
|
final String message;
|
|
|
|
/// Word-by-word comparison with the dictated sentence; null otherwise.
|
|
final SpokenComparison? dictation;
|
|
}
|
|
|
|
/// Checks only the minimum information a learner needs to produce in a
|
|
/// review. The rule and the hint come from the core item's course JSON, which
|
|
/// accepts contractions and variable slot values instead of one fixed
|
|
/// sentence.
|
|
class ReviewFeedback {
|
|
const ReviewFeedback._();
|
|
|
|
static ReviewCheckResult check(ReviewItem item, String input) {
|
|
final dictation = item.skill == dictationSkill
|
|
? coreDictationSentences[item.id]
|
|
: null;
|
|
if (dictation != null) {
|
|
final comparison = compareSpoken(dictation.sentence, input);
|
|
final missing = comparison.total - comparison.heardCount;
|
|
final wrong = missing + comparison.extraWords.length;
|
|
return ReviewCheckResult(
|
|
complete: comparison.matches,
|
|
message: comparison.matches
|
|
? '听写正确。'
|
|
: '有 $wrong 处和听到的句子不一致。再听一次,检查拼写和漏掉的词。',
|
|
dictation: comparison,
|
|
);
|
|
}
|
|
final complete = isCoreItem(item.id)
|
|
? coreItemUsedIn(item.id, input)
|
|
: RegExp(r"[a-z]+(?:'[a-z]+)?")
|
|
.allMatches(normalizeAnswer(input))
|
|
.map((match) => match.group(0))
|
|
.toSet()
|
|
.length >=
|
|
2;
|
|
return ReviewCheckResult(
|
|
complete: complete,
|
|
message: complete
|
|
? '表达已完成,可以进入下一次复习安排。'
|
|
: coreReviewHints[item.id] ?? '再补充本次目标中的关键英文词或句型。',
|
|
);
|
|
}
|
|
}
|