整个课程和数据UI重新设计版本
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'courses/courses.dart';
|
||||
|
||||
class WritingCheckResult {
|
||||
const WritingCheckResult({required this.complete, required this.message});
|
||||
|
||||
@@ -5,84 +7,24 @@ class WritingCheckResult {
|
||||
final String message;
|
||||
}
|
||||
|
||||
/// A deliberately small offline checker for teaching tasks. It checks only
|
||||
/// the essential A0 information, accepts variable names/places, and never
|
||||
/// claims to grade pronunciation or nuanced grammar.
|
||||
/// A deliberately small offline checker for teaching tasks. The rule and the
|
||||
/// hint come from the segment's writing task in the course JSON; it
|
||||
/// checks only the essential information and never claims to grade
|
||||
/// pronunciation or nuanced grammar.
|
||||
class WritingFeedback {
|
||||
const WritingFeedback._();
|
||||
|
||||
static String _normalize(String input) => input
|
||||
.toLowerCase()
|
||||
.replaceAll('’', "'")
|
||||
.replaceAll('‘', "'")
|
||||
.replaceAll(RegExp(r'\s+'), ' ')
|
||||
.trim();
|
||||
|
||||
static WritingCheckResult check(
|
||||
String lessonId,
|
||||
String input, {
|
||||
String? segmentId,
|
||||
}) {
|
||||
final text = _normalize(input);
|
||||
final words = RegExp(
|
||||
r"[a-z]+(?:'[a-z]+)?",
|
||||
).allMatches(text).map((match) => match.group(0)!).toSet();
|
||||
bool has(String word) => words.contains(_normalize(word));
|
||||
|
||||
bool hasPhrase(String phrase) => text.contains(_normalize(phrase));
|
||||
|
||||
bool hasAny(Iterable<String> choices) => choices.any(has);
|
||||
final hasIntroduction =
|
||||
hasPhrase("i'm") || hasPhrase('i am') || hasPhrase('my name is');
|
||||
final hasItIs = hasPhrase("it's") || hasPhrase('it is');
|
||||
|
||||
final complete = switch (segmentId) {
|
||||
'a0-04-a' => hasAll(words, ['zero', 'one', 'two', 'three']),
|
||||
'a0-04-b' => hasAll(words, ['six', 'seven', 'eight']),
|
||||
'a0-04-c' =>
|
||||
RegExp(
|
||||
r'\b(one|two|three|four|five|six|seven|eight|nine|zero)\b',
|
||||
).allMatches(text).length >=
|
||||
3,
|
||||
'a0-08-a' => hasItIs && hasAny(['monday', 'tuesday', 'wednesday']),
|
||||
'a0-08-b' =>
|
||||
hasItIs && hasAny(['thursday', 'friday', 'saturday', 'sunday']),
|
||||
'a0-08-c' => hasItIs && (has('oclock') || hasPhrase("o'clock")),
|
||||
_ => switch (lessonId) {
|
||||
'a0-01' => hasAny(['hello', 'hi']) && hasIntroduction,
|
||||
'a0-02' =>
|
||||
(hasPhrase('my name') || hasIntroduction) &&
|
||||
RegExp(r'[a-z](?:[ -]?[a-z]){2,}').hasMatch(text),
|
||||
'a0-03' => hasIntroduction && hasAny(['good', 'okay', 'tired']),
|
||||
'a0-04' =>
|
||||
RegExp(
|
||||
r'\b(zero|one|two|three|four|five|six|seven|eight|nine|ten)\b',
|
||||
).allMatches(text).length >=
|
||||
3,
|
||||
'a0-05' => hasItIs && hasAny(['book', 'pen', 'bag', 'key']),
|
||||
'a0-06' => hasIntroduction && has('from') && words.length >= 3,
|
||||
'a0-07' =>
|
||||
hasPhrase('this is my') &&
|
||||
hasAny(['mother', 'father', 'sister', 'brother', 'friend']),
|
||||
'a0-08' =>
|
||||
hasItIs &&
|
||||
(hasAny([
|
||||
'monday',
|
||||
'tuesday',
|
||||
'wednesday',
|
||||
'thursday',
|
||||
'friday',
|
||||
'saturday',
|
||||
'sunday',
|
||||
]) ||
|
||||
has('clock')),
|
||||
'a0-09' => hasIntroduction && has('like') && words.length >= 3,
|
||||
'a0-10' =>
|
||||
hasIntroduction && has('from') && has('like') && has('water'),
|
||||
_ => words.length >= 2,
|
||||
},
|
||||
};
|
||||
|
||||
static WritingCheckResult check(String segmentId, String input) {
|
||||
final activity = activityBySegmentId(segmentId);
|
||||
final rule = activity.writingRequiredTerms;
|
||||
final complete = rule.isEmpty
|
||||
? RegExp(r"[a-z]+(?:'[a-z]+)?")
|
||||
.allMatches(normalizeAnswer(input))
|
||||
.map((match) => match.group(0))
|
||||
.toSet()
|
||||
.length >=
|
||||
2
|
||||
: matchesRule(input, rule);
|
||||
if (complete) {
|
||||
return const WritingCheckResult(
|
||||
complete: true,
|
||||
@@ -91,30 +33,9 @@ class WritingFeedback {
|
||||
}
|
||||
return WritingCheckResult(
|
||||
complete: false,
|
||||
message: _hintFor(segmentId ?? lessonId),
|
||||
message: activity.writingHint.isNotEmpty
|
||||
? activity.writingHint
|
||||
: '再补充一个完整英文句子。',
|
||||
);
|
||||
}
|
||||
|
||||
static bool hasAll(Set<String> words, Iterable<String> required) =>
|
||||
required.every(words.contains);
|
||||
|
||||
static String _hintFor(String lessonId) => switch (lessonId) {
|
||||
'a0-04-a' => '请写出 zero、one、two、three 四个数字。',
|
||||
'a0-04-b' => '请写出 six、seven、eight 三个数字。',
|
||||
'a0-04-c' => '用英文一个一个写出三个数字,例如 one-three-nine。',
|
||||
'a0-08-a' => '用 It is / It’s 加 Monday、Tuesday 或 Wednesday。',
|
||||
'a0-08-b' => '用 It is / It’s 加 Thursday、Friday、Saturday 或 Sunday。',
|
||||
'a0-08-c' => '用 It is / It’s 加数字和 o’clock,例如 It’s three o’clock。',
|
||||
'a0-01' => '试着同时写一句问候和姓名,例如:Hello. I’m …',
|
||||
'a0-02' => '写出姓名并拼写一遍,例如 My name is Shen. S-H-E-N.',
|
||||
'a0-03' => '用 I’m / I am 加上你的状态,例如 good 或 tired。',
|
||||
'a0-04' => '用英文写出三个数字,例如 one-three-nine。',
|
||||
'a0-05' => '用 It’s / It is 加一个物品,例如 a pen。',
|
||||
'a0-06' => '用 I’m from … 写成一个完整句子。',
|
||||
'a0-07' => '用 This is my … 介绍一位家人或朋友。',
|
||||
'a0-08' => '用 It’s … 写一个星期或整点时间。',
|
||||
'a0-09' => '用 I like … 写出一种喜好。',
|
||||
'a0-10' => '分别写姓名、来自哪里和喜好三部分,喜好用 water:I like water.',
|
||||
_ => '再补充一个完整英文句子。',
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user