setup for grammar error practice

This commit is contained in:
ggurdin 2026-01-19 15:34:20 -05:00
parent 567b0450ba
commit 33b05f6f24
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
7 changed files with 98 additions and 4 deletions

View file

@ -5051,5 +5051,7 @@
"practiceGrammar": "Practice grammar",
"notEnoughToPractice": "Send more messages to unlock practice",
"constructUseCorGCDesc": "Correct grammar category practice",
"constructUseIncGCDesc": "Incorrect grammar category practice"
"constructUseIncGCDesc": "Incorrect grammar category practice",
"constructUseCorGEDesc": "Correct grammar error practice",
"constructUseIncGEDesc": "Incorrect grammar error practice"
}

View file

@ -86,6 +86,10 @@ enum ConstructUseTypeEnum {
// grammar category activity
corGC,
incGC,
// grammar error activity
corGE,
incGE,
}
extension ConstructUseTypeExtension on ConstructUseTypeEnum {
@ -171,6 +175,10 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
return L10n.of(context).constructUseCorGCDesc;
case ConstructUseTypeEnum.incGC:
return L10n.of(context).constructUseIncGCDesc;
case ConstructUseTypeEnum.corGE:
return L10n.of(context).constructUseCorGEDesc;
case ConstructUseTypeEnum.incGE:
return L10n.of(context).constructUseIncGEDesc;
}
}
@ -213,6 +221,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.ignM:
case ConstructUseTypeEnum.corGC:
case ConstructUseTypeEnum.incGC:
case ConstructUseTypeEnum.corGE:
case ConstructUseTypeEnum.incGE:
return ActivityTypeEnum.morphId.icon;
case ConstructUseTypeEnum.em:
return ActivityTypeEnum.emoji.icon;
@ -246,6 +256,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.corLM:
case ConstructUseTypeEnum.corLA:
case ConstructUseTypeEnum.corGC:
case ConstructUseTypeEnum.corGE:
return 5;
case ConstructUseTypeEnum.pvm:
@ -287,6 +298,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.incLM:
case ConstructUseTypeEnum.incLA:
case ConstructUseTypeEnum.incGC:
case ConstructUseTypeEnum.incGE:
return -1;
case ConstructUseTypeEnum.incPA:
@ -340,6 +352,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.bonus:
case ConstructUseTypeEnum.corGC:
case ConstructUseTypeEnum.incGC:
case ConstructUseTypeEnum.corGE:
case ConstructUseTypeEnum.incGE:
return false;
}
}
@ -385,6 +399,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.incLM:
case ConstructUseTypeEnum.corGC:
case ConstructUseTypeEnum.incGC:
case ConstructUseTypeEnum.corGE:
case ConstructUseTypeEnum.incGE:
return LearningSkillsEnum.reading;
case ConstructUseTypeEnum.pvm:
return LearningSkillsEnum.speaking;
@ -415,6 +431,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.corLM:
case ConstructUseTypeEnum.corLA:
case ConstructUseTypeEnum.corGC:
case ConstructUseTypeEnum.corGE:
return SpaceAnalyticsSummaryEnum.numChoicesCorrect;
case ConstructUseTypeEnum.incIt:
@ -428,6 +445,7 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum {
case ConstructUseTypeEnum.incLM:
case ConstructUseTypeEnum.incLA:
case ConstructUseTypeEnum.incGC:
case ConstructUseTypeEnum.incGE:
return SpaceAnalyticsSummaryEnum.numChoicesIncorrect;
case ConstructUseTypeEnum.ignIt:

View file

@ -0,0 +1,42 @@
import 'package:fluffychat/pangea/practice_activities/message_activity_request.dart';
import 'package:fluffychat/pangea/practice_activities/multiple_choice_activity_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
import 'package:fluffychat/widgets/matrix.dart';
class GrammarErrorPracticeGenerator {
static Future<MessageActivityResponse> get(
MessageActivityRequest req,
) async {
final igcMatch = target.igcMatch;
assert(igcMatch.bestChoice != null, 'IGC match must have a best choice');
assert(igcMatch.choices != null, 'IGC match must have choices');
final errorSpan = igcMatch.errorSpan;
final correctChoice = igcMatch.bestChoice!.value;
final choices = igcMatch.choices!.map((c) => c.value).toList();
final choiceTokens = target.tokens.where(
(token) => choices.any(
(choice) => choice.contains(token.text.content),
),
);
assert(
choiceTokens.isNotEmpty,
'At least one token should match the error choices',
);
choices.add(errorSpan);
choices.shuffle();
return MessageActivityResponse(
activity: GrammarErrorPracticeActivityModel(
tokens: choiceTokens.toList(),
langCode: req.userL2,
multipleChoiceContent: MultipleChoiceActivity(
choices: choices.toSet(),
answers: {correctChoice},
),
),
);
}
}

View file

@ -132,6 +132,9 @@ class SpanData {
return choices![index];
}
String get errorSpan =>
fullText.characters.skip(offset).take(length).toString();
bool isNormalizationError() {
final correctChoice = choices
?.firstWhereOrNull(
@ -139,8 +142,6 @@ class SpanData {
)
?.value;
final errorSpan = fullText.characters.skip(offset).take(length).toString();
final l2Code =
MatrixState.pangeaController.userController.userL2?.langCodeShort;

View file

@ -15,7 +15,8 @@ enum ActivityTypeEnum {
messageMeaning,
lemmaMeaning,
lemmaAudio,
grammarCategory;
grammarCategory,
grammarError;
bool get includeTTSOnClick {
switch (this) {
@ -30,6 +31,7 @@ enum ActivityTypeEnum {
case ActivityTypeEnum.lemmaAudio:
case ActivityTypeEnum.lemmaMeaning:
case ActivityTypeEnum.grammarCategory:
case ActivityTypeEnum.grammarError:
return true;
}
}
@ -68,6 +70,9 @@ enum ActivityTypeEnum {
case 'grammar_category':
case 'grammarCategory':
return ActivityTypeEnum.grammarCategory;
case 'grammar_error':
case 'grammarError':
return ActivityTypeEnum.grammarError;
default:
throw Exception('Unknown activity type: $split');
}
@ -128,6 +133,11 @@ enum ActivityTypeEnum {
ConstructUseTypeEnum.corGC,
ConstructUseTypeEnum.incGC,
];
case ActivityTypeEnum.grammarError:
return [
ConstructUseTypeEnum.corGE,
ConstructUseTypeEnum.incGE,
];
}
}
@ -153,6 +163,8 @@ enum ActivityTypeEnum {
return ConstructUseTypeEnum.corLM;
case ActivityTypeEnum.grammarCategory:
return ConstructUseTypeEnum.corGC;
case ActivityTypeEnum.grammarError:
return ConstructUseTypeEnum.corGE;
}
}
@ -178,6 +190,8 @@ enum ActivityTypeEnum {
return ConstructUseTypeEnum.incLM;
case ActivityTypeEnum.grammarCategory:
return ConstructUseTypeEnum.incGC;
case ActivityTypeEnum.grammarError:
return ConstructUseTypeEnum.incGE;
}
}
@ -198,6 +212,7 @@ enum ActivityTypeEnum {
return Icons.format_shapes;
case ActivityTypeEnum.messageMeaning:
case ActivityTypeEnum.grammarCategory:
case ActivityTypeEnum.grammarError:
return Icons.star; // TODO: Add to L10n
}
}
@ -217,6 +232,7 @@ enum ActivityTypeEnum {
case ActivityTypeEnum.lemmaMeaning:
case ActivityTypeEnum.lemmaAudio:
case ActivityTypeEnum.grammarCategory:
case ActivityTypeEnum.grammarError:
return 1;
}
}
@ -235,6 +251,7 @@ enum ActivityTypeEnum {
static List<ActivityTypeEnum> get _grammarPracticeTypes => [
ActivityTypeEnum.grammarCategory,
ActivityTypeEnum.grammarError,
];
static List<ActivityTypeEnum> analyticsPracticeTypes(

View file

@ -48,6 +48,8 @@ sealed class PracticeActivityModel {
return ActivityTypeEnum.morphId;
case WordListeningPracticeActivityModel():
return ActivityTypeEnum.wordFocusListening;
case GrammarErrorPracticeActivityModel():
return ActivityTypeEnum.grammarError;
}
}
@ -350,6 +352,15 @@ class LemmaPracticeActivityModel extends MultipleChoicePracticeActivityModel {
});
}
class GrammarErrorPracticeActivityModel
extends MultipleChoicePracticeActivityModel {
GrammarErrorPracticeActivityModel({
required super.tokens,
required super.langCode,
required super.multipleChoiceContent,
});
}
class EmojiPracticeActivityModel extends MatchPracticeActivityModel {
EmojiPracticeActivityModel({
required super.tokens,

View file

@ -9,6 +9,7 @@ import 'package:async/async.dart';
import 'package:get_storage/get_storage.dart';
import 'package:http/http.dart';
import 'package:fluffychat/pangea/analytics_practice/grammar_error_practice_generator.dart';
import 'package:fluffychat/pangea/analytics_practice/morph_category_activity_generator.dart';
import 'package:fluffychat/pangea/analytics_practice/vocab_audio_activity_generator.dart';
import 'package:fluffychat/pangea/analytics_practice/vocab_meaning_activity_generator.dart';
@ -128,6 +129,8 @@ class PracticeRepo {
return VocabAudioActivityGenerator.get(req);
case ActivityTypeEnum.grammarCategory:
return MorphCategoryActivityGenerator.get(req);
case ActivityTypeEnum.grammarError:
return GrammarErrorPracticeGenerator.get(req);
case ActivityTypeEnum.morphId:
return MorphActivityGenerator.get(req);
case ActivityTypeEnum.wordMeaning: