feat: 完善听力口语辅助状态记录、练习分块与复习抽查间隔机制
This commit is contained in:
@@ -22,7 +22,9 @@ class AssessmentTask {
|
||||
skill: AssessmentSkill.values.byName(json['skill'] as String),
|
||||
prompt: json['prompt'] as String? ?? '',
|
||||
audio: json['audio'] as String?,
|
||||
choices: [for (final choice in json['choices'] as List? ?? const []) '$choice'],
|
||||
choices: [
|
||||
for (final choice in json['choices'] as List? ?? const []) '$choice',
|
||||
],
|
||||
answerIndex: json['answerIndex'] as int?,
|
||||
accept: [
|
||||
for (final group in json['accept'] as List? ?? const [])
|
||||
@@ -102,10 +104,7 @@ AssessmentPack? replacementFor(String packId) =>
|
||||
/// The pack whose results [packId] counts toward: the original of a
|
||||
/// replacement pack, otherwise [packId] itself.
|
||||
String canonicalAssessmentPackId(String packId) =>
|
||||
assessmentPacks
|
||||
.where((pack) => pack.id == packId)
|
||||
.firstOrNull
|
||||
?.replaces ??
|
||||
assessmentPacks.where((pack) => pack.id == packId).firstOrNull?.replaces ??
|
||||
packId;
|
||||
|
||||
/// Local check of an open task with the same rule engine as the lessons: it
|
||||
|
||||
@@ -63,13 +63,7 @@ void registerSavedWord(
|
||||
required String zh,
|
||||
required String level,
|
||||
}) {
|
||||
receptiveWordRegistry[id] = (
|
||||
level: level,
|
||||
unit: '',
|
||||
en: en,
|
||||
zh: zh,
|
||||
ipa: '',
|
||||
);
|
||||
receptiveWordRegistry[id] = (level: level, unit: '', en: en, zh: zh, ipa: '');
|
||||
}
|
||||
|
||||
/// Registers one word of the standalone word bank. Unlike a unit's receptive
|
||||
@@ -191,9 +185,10 @@ WordQuestion? receptiveQuestion(
|
||||
if (wrong.isEmpty) return null;
|
||||
// A different variant asks the same word against other meanings, which is
|
||||
// what 「换题、换语境」 means for a word this small.
|
||||
final rotated = [...wrong.skip(variant % wrong.length), ...wrong.take(variant % wrong.length)]
|
||||
.take(2)
|
||||
.toList();
|
||||
final rotated = [
|
||||
...wrong.skip(variant % wrong.length),
|
||||
...wrong.take(variant % wrong.length),
|
||||
].take(2).toList();
|
||||
// Asked from the meaning, the options are spellings and the prompt is the
|
||||
// Chinese; asked either other way, it is the other way round.
|
||||
final asksSpelling = mode == WordAskMode.recall;
|
||||
@@ -339,7 +334,6 @@ const spokenRecallSkill = '口头回忆';
|
||||
/// review. Passing it is 「认识」 evidence and never advances a checkpoint.
|
||||
const recognitionGateSkill = '听辨识别';
|
||||
|
||||
|
||||
/// The skill of a contrast question: a situation is described and the learner
|
||||
/// picks which taught sentence it calls for. It shows the learner knows *when*
|
||||
/// to say something, which is not the same as being able to produce it, so it
|
||||
|
||||
@@ -12,7 +12,6 @@ class LessonSegment {
|
||||
this.estimatedMinutes = 12,
|
||||
});
|
||||
|
||||
|
||||
final String id;
|
||||
final List<String> targetItemIds;
|
||||
final List<String> previewItemIds;
|
||||
@@ -86,8 +85,12 @@ class LessonActivity {
|
||||
required this.listening,
|
||||
required this.listeningQuestion,
|
||||
required this.answers,
|
||||
this.listeningItemIds = const [],
|
||||
required this.speaking,
|
||||
this.speakingTip = '',
|
||||
this.speakingChunks = const [],
|
||||
this.speakingItemIds = const [],
|
||||
this.speakingExamples = const [],
|
||||
required this.reading,
|
||||
required this.readingQuestion,
|
||||
required this.readingAnswer,
|
||||
@@ -100,14 +103,17 @@ class LessonActivity {
|
||||
required this.independentHelp,
|
||||
});
|
||||
|
||||
|
||||
final String listening;
|
||||
final String listeningQuestion;
|
||||
final List<String> answers;
|
||||
final List<String> listeningItemIds;
|
||||
final String speaking;
|
||||
|
||||
/// 这句跟读的发音提示;为空时不显示,避免给每一句套用同一条提示。
|
||||
final String speakingTip;
|
||||
final List<String> speakingChunks;
|
||||
final List<String> speakingItemIds;
|
||||
final List<String> speakingExamples;
|
||||
final String reading;
|
||||
final String readingQuestion;
|
||||
final String readingAnswer;
|
||||
@@ -135,7 +141,6 @@ class LessonDialogue {
|
||||
this.taskLabels = const [],
|
||||
});
|
||||
|
||||
|
||||
final String goal;
|
||||
final List<String> prompts;
|
||||
final List<String> hints;
|
||||
@@ -165,7 +170,6 @@ class DialogueScene {
|
||||
this.unlockAfterLessonId,
|
||||
});
|
||||
|
||||
|
||||
final String id;
|
||||
final String title;
|
||||
final String summary;
|
||||
|
||||
@@ -214,8 +214,12 @@ class PackSegment {
|
||||
required this.listeningText,
|
||||
required this.listeningQuestion,
|
||||
required this.listeningOptions,
|
||||
this.listeningItemIds = const [],
|
||||
required this.speakingText,
|
||||
required this.speakingTip,
|
||||
this.speakingChunks = const [],
|
||||
this.speakingItemIds = const [],
|
||||
this.speakingExamples = const [],
|
||||
required this.readingText,
|
||||
required this.readingQuestion,
|
||||
required this.readingOptions,
|
||||
@@ -241,9 +245,13 @@ class PackSegment {
|
||||
final String listeningText;
|
||||
final String listeningQuestion;
|
||||
final List<String> listeningOptions;
|
||||
final List<String> listeningItemIds;
|
||||
|
||||
final String speakingText;
|
||||
final String speakingTip;
|
||||
final List<String> speakingChunks;
|
||||
final List<String> speakingItemIds;
|
||||
final List<String> speakingExamples;
|
||||
|
||||
final String readingText;
|
||||
final String readingQuestion;
|
||||
@@ -288,8 +296,15 @@ class PackSegment {
|
||||
listeningText: listening['text'] as String? ?? '',
|
||||
listeningQuestion: listening['question'] as String? ?? '',
|
||||
listeningOptions: _stringList(listening['options']),
|
||||
listeningItemIds: _stringList(listening['itemIds']),
|
||||
speakingText: firstSpeaking['text'] as String? ?? '',
|
||||
speakingTip: firstSpeaking['tip'] as String? ?? '',
|
||||
speakingChunks: _stringList(firstSpeaking['chunks']),
|
||||
speakingItemIds: _stringList(firstSpeaking['itemIds']),
|
||||
speakingExamples: [
|
||||
for (final line in speaking.skip(1))
|
||||
if (line['text'] is String) line['text'] as String,
|
||||
],
|
||||
readingText: reading['text'] as String? ?? '',
|
||||
readingQuestion: reading['question'] as String? ?? '',
|
||||
readingOptions: _stringList(reading['options']),
|
||||
|
||||
@@ -225,8 +225,12 @@ class CourseRepository {
|
||||
answers: seg.listeningOptions.isNotEmpty
|
||||
? seg.listeningOptions
|
||||
: const [''],
|
||||
listeningItemIds: seg.listeningItemIds,
|
||||
speaking: seg.speakingText,
|
||||
speakingTip: seg.speakingTip,
|
||||
speakingChunks: seg.speakingChunks,
|
||||
speakingItemIds: seg.speakingItemIds,
|
||||
speakingExamples: seg.speakingExamples,
|
||||
reading: seg.readingText,
|
||||
readingQuestion: seg.readingQuestion,
|
||||
readingAnswer: seg.readingOptions.isNotEmpty
|
||||
|
||||
Reference in New Issue
Block a user