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] ?? '再补充本次目标中的关键英文词或句型。', ); } }