fix(reading_assistance): fixed a bug in scoring matches

pull/1817/head
wcjord 7 months ago
parent c60b16dfe2
commit 88369ba8f7

@ -1,9 +1,7 @@
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
abstract class AppConfig { abstract class AppConfig {
// #Pangea // #Pangea
// static String _applicationName = 'FluffyChat'; // static String _applicationName = 'FluffyChat';
@ -55,7 +53,8 @@ abstract class AppConfig {
// static const Color primaryColorLight = Color(0xFFCCBDEA); // static const Color primaryColorLight = Color(0xFFCCBDEA);
static const Color primaryColor = Color(0xFF8560E0); static const Color primaryColor = Color(0xFF8560E0);
static const Color primaryColorLight = Color(0xFFDBC9FF); static const Color primaryColorLight = Color(0xFFDBC9FF);
static const Color secondaryColor = Color(0xFF41a2bc); // static const Color secondaryColor = Color(0xFF41a2bc);
static const Color secondaryColor = Color.fromARGB(255, 253, 191, 1);
static const Color activeToggleColor = Color(0xFF33D057); static const Color activeToggleColor = Color(0xFF33D057);
static const Color success = Color(0xFF33D057); static const Color success = Color(0xFF33D057);
static const Color warning = Color.fromARGB(255, 210, 124, 12); static const Color warning = Color.fromARGB(255, 210, 124, 12);

@ -68,6 +68,8 @@ abstract class FluffyThemes {
seedColor: seed ?? seedColor: seed ??
AppConfig.colorSchemeSeed ?? AppConfig.colorSchemeSeed ??
Theme.of(context).colorScheme.primary, Theme.of(context).colorScheme.primary,
// primary: AppConfig.primaryColor,
// secondary: AppConfig.gold,
); );
final isColumnMode = FluffyThemes.isColumnMode(context); final isColumnMode = FluffyThemes.isColumnMode(context);
return ThemeData( return ThemeData(

@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class InstructionsInlineTooltip extends StatefulWidget { class InstructionsInlineTooltip extends StatefulWidget {
final InstructionsEnum instructionsEnum; final InstructionsEnum instructionsEnum;
@ -103,7 +101,7 @@ class InstructionsInlineTooltipState extends State<InstructionsInlineTooltip>
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppConfig.borderRadius), borderRadius: BorderRadius.circular(AppConfig.borderRadius),
color: Color.alphaBlend( color: Color.alphaBlend(
Colors.black.withAlpha(70), Theme.of(context).colorScheme.surface.withAlpha(70),
AppConfig.gold, AppConfig.gold,
), ),
), ),

@ -1,13 +1,7 @@
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart'; import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart';
@ -23,6 +17,10 @@ import 'package:fluffychat/pangea/practice_activities/practice_record.dart';
import 'package:fluffychat/pangea/practice_activities/practice_target.dart'; import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
import 'package:fluffychat/pangea/practice_activities/relevant_span_display_details.dart'; import 'package:fluffychat/pangea/practice_activities/relevant_span_display_details.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
class PracticeActivityModel { class PracticeActivityModel {
List<PangeaToken> targetTokens; List<PangeaToken> targetTokens;
@ -95,9 +93,9 @@ class PracticeActivityModel {
score: isCorrect ? 1 : 0, score: isCorrect ? 1 : 0,
); );
debugPrint( // debugPrint(
"onMultipleChoiceSelect: ${choice.form} ${responseUseType(choice)}", // "onMultipleChoiceSelect: ${choice.form} ${responseUseType(choice)}",
); // );
MatrixState.pangeaController.putAnalytics.setState( MatrixState.pangeaController.putAnalytics.setState(
AnalyticsStream( AnalyticsStream(
@ -105,7 +103,7 @@ class PracticeActivityModel {
roomId: event?.room.id, roomId: event?.room.id,
constructs: [ constructs: [
OneConstructUse( OneConstructUse(
useType: responseUseType(choice)!, useType: practiceTarget.record.responses.last.useType(activityType),
lemma: choice.form.cId.lemma, lemma: choice.form.cId.lemma,
constructType: choice.form.cId.type, constructType: choice.form.cId.type,
metadata: ConstructUseMetaData( metadata: ConstructUseMetaData(
@ -133,8 +131,12 @@ class PracticeActivityModel {
) { ) {
// the user has already selected this choice // the user has already selected this choice
// so we don't want to record it again // so we don't want to record it again
if (practiceTarget.record.hasTextResponse(choice.choiceContent) || if (practiceTarget.record.alreadyHasMatchResponse(
token.vocabConstructID,
choice.choiceContent,
) ||
isComplete) { isComplete) {
debugger(when: kDebugMode);
return; return;
} }
@ -144,8 +146,12 @@ class PracticeActivityModel {
(answer) => answer.toLowerCase() == choice.choiceContent.toLowerCase(), (answer) => answer.toLowerCase() == choice.choiceContent.toLowerCase(),
); );
} else if (matchContent != null) { } else if (matchContent != null) {
isCorrect = matchContent!.matchInfo[token.vocabForm]! // we check to see if it's in the list of acceptable answers
.contains(choice.choiceContent); // rather than if the vocabForm is the same because an emoji
// could be in multiple constructs so there could be multiple answers
final answers = matchContent!.matchInfo[token.vocabForm];
debugger(when: answers == null && kDebugMode);
isCorrect = answers!.contains(choice.choiceContent);
} else { } else {
debugger(when: kDebugMode); debugger(when: kDebugMode);
ErrorHandler.logError( ErrorHandler.logError(
@ -156,11 +162,11 @@ class PracticeActivityModel {
return; return;
} }
// NOTE: the response is associated with the contructId of the choice, not the selected token // NOTE: the response is associated with the contructId of the selected token, not the choice
// example: the user selects the word "cat" to match with the emoji 🐶 // example: the user selects the word "cat" to match with the emoji 🐶
// the response is associated with correct word "dog", not the word "cat" // the response is associated with incorrect word "cat", not the word "dog"
practiceTarget.record.addResponse( practiceTarget.record.addResponse(
cId: choice.form.cId, cId: token.vocabConstructID,
text: choice.choiceContent, text: choice.choiceContent,
target: practiceTarget, target: practiceTarget,
score: isCorrect ? 1 : 0, score: isCorrect ? 1 : 0,
@ -174,15 +180,16 @@ class PracticeActivityModel {
roomId: event?.room.id, roomId: event?.room.id,
constructs: [ constructs: [
OneConstructUse( OneConstructUse(
useType: responseUseType(choice)!, useType:
lemma: choice.form.cId.lemma, practiceTarget.record.responses.last.useType(activityType),
constructType: choice.form.cId.type, lemma: token.lemma.text,
constructType: ConstructTypeEnum.vocab,
metadata: ConstructUseMetaData( metadata: ConstructUseMetaData(
roomId: event?.room.id, roomId: event?.room.id,
timeStamp: DateTime.now(), timeStamp: DateTime.now(),
eventId: event?.eventId, eventId: event?.eventId,
), ),
category: choice.form.cId.category, category: token.pos,
// in the case of a wrong answer, the cId doesn't match the token // in the case of a wrong answer, the cId doesn't match the token
form: token.text.content, form: token.text.content,
), ),
@ -226,27 +233,18 @@ class PracticeActivityModel {
/// if null, it means the user has not yet responded with that choice /// if null, it means the user has not yet responded with that choice
bool? wasCorrectMatch(PracticeChoice choice) { bool? wasCorrectMatch(PracticeChoice choice) {
for (final response in practiceTarget.record.responses) { for (final response in practiceTarget.record.responses) {
if (response.cId == choice.form.cId && response.isCorrect) { if (response.text == choice.choiceContent && response.isCorrect) {
return true; return true;
} }
} }
for (final response in practiceTarget.record.responses) { for (final response in practiceTarget.record.responses) {
if (response.cId == choice.form.cId) { if (response.text == choice.choiceContent) {
return false; return false;
} }
} }
return null; return null;
} }
ConstructUseTypeEnum? responseUseType(PracticeChoice choice) {
for (final response in practiceTarget.record.responses) {
if (response.cId == choice.form.cId) {
return response.useType(activityType);
}
}
return null;
}
PracticeRecord get record => practiceTarget.record; PracticeRecord get record => practiceTarget.record;
PracticeTarget get practiceTarget => PracticeTarget( PracticeTarget get practiceTarget => PracticeTarget(

@ -2,12 +2,6 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pangea/common/config/environment.dart'; import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/common/network/requests.dart'; import 'package:fluffychat/pangea/common/network/requests.dart';
@ -25,6 +19,10 @@ import 'package:fluffychat/pangea/practice_activities/practice_activity_model.da
import 'package:fluffychat/pangea/practice_activities/word_focus_listening_generator.dart'; import 'package:fluffychat/pangea/practice_activities/word_focus_listening_generator.dart';
import 'package:fluffychat/pangea/toolbar/event_wrappers/practice_activity_event.dart'; import 'package:fluffychat/pangea/toolbar/event_wrappers/practice_activity_event.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:matrix/matrix.dart';
/// Represents an item in the completion cache. /// Represents an item in the completion cache.
class _RequestCacheItem { class _RequestCacheItem {
@ -173,7 +171,6 @@ class PracticeRepo {
// TODO resolve some wierdness here whereby the activity can be null but then... it's not // TODO resolve some wierdness here whereby the activity can be null but then... it's not
final eventCompleter = Completer<PracticeActivityEvent?>(); final eventCompleter = Completer<PracticeActivityEvent?>();
debugPrint('Activity generated: ${res.activity.toJson()}');
if (event != null) { if (event != null) {
_sendAndPackageEvent(res.activity, event).then((event) { _sendAndPackageEvent(res.activity, event).then((event) {
eventCompleter.complete(event); eventCompleter.complete(event);

@ -1,7 +1,7 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/constructs/construct_form.dart'; import 'package:fluffychat/pangea/constructs/construct_form.dart';
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart'; import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
import 'package:flutter/material.dart';
class PracticeMatchActivity { class PracticeMatchActivity {
/// The constructIdenfifiers involved in the activity /// The constructIdenfifiers involved in the activity
@ -13,6 +13,11 @@ class PracticeMatchActivity {
PracticeMatchActivity({ PracticeMatchActivity({
required this.matchInfo, required this.matchInfo,
}) { }) {
for (final ith in matchInfo.entries) {
debugPrint(
'Construct: ${ith.key}, Forms: ${ith.value}',
);
}
// if there are multiple forms for a construct, pick one to display // if there are multiple forms for a construct, pick one to display
// each cosntruct will have ~3 forms // each cosntruct will have ~3 forms
// sometimes a form could be in multiple constructs // sometimes a form could be in multiple constructs
@ -21,13 +26,17 @@ class PracticeMatchActivity {
// either from that construct's options, or returning to the previous construct // either from that construct's options, or returning to the previous construct
// and picking a different form from there // and picking a different form from there
for (final ith in matchInfo.entries) { for (final ith in matchInfo.entries) {
for (final acceptableAnswer in ith.value) { for (int i = 0; i < ith.value.length; i++) {
final String acceptableAnswer = ith.value[i];
if (!choices if (!choices
.any((element) => element.choiceContent == acceptableAnswer)) { .any((element) => element.choiceContent == acceptableAnswer)) {
choices.add( choices.add(
PracticeChoice(choiceContent: acceptableAnswer, form: ith.key), PracticeChoice(choiceContent: acceptableAnswer, form: ith.key),
); );
break; debugPrint(
'Added choice: ${choices.last.choiceContent} for form: ${choices.last.form.form}',
);
i = ith.value.length; // break out of the loop
} }
// TODO: if none found, we can probably pick a different form for the other one // TODO: if none found, we can probably pick a different form for the other one
} }

@ -5,8 +5,6 @@
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart'; import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
@ -16,6 +14,7 @@ import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_record_repo.dart'; import 'package:fluffychat/pangea/practice_activities/practice_record_repo.dart';
import 'package:fluffychat/pangea/practice_activities/practice_target.dart'; import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
import 'package:flutter/foundation.dart';
class PracticeRecord { class PracticeRecord {
late DateTime createdAt; late DateTime createdAt;
@ -72,6 +71,15 @@ class PracticeRecord {
return responses.any((element) => element.text == text); return responses.any((element) => element.text == text);
} }
bool alreadyHasMatchResponse(
ConstructIdentifier cId,
String text,
) {
return responses.any(
(element) => element.cId == cId && element.text == text,
);
}
/// [target] needed for saving the record, little funky /// [target] needed for saving the record, little funky
/// [cId] identifies the construct in the case of match activities which have multiple /// [cId] identifies the construct in the case of match activities which have multiple
/// [text] is the user's response /// [text] is the user's response
@ -111,6 +119,7 @@ class PracticeRecord {
score: score, score: score,
), ),
); );
debugPrint("responses: ${responses.map((r) => r.toJson())}");
PracticeRecordRepo.save(target, this); PracticeRecordRepo.save(target, this);
} catch (e) { } catch (e) {
@ -156,6 +165,9 @@ class PracticeRecord {
} }
class ActivityRecordResponse { class ActivityRecordResponse {
/// the cId of the construct that the user attached their response to
/// ie. in the "I like the dog" if the user erroneously attaches a dog emoji to the word like
/// then the cId is that of 'like
ConstructIdentifier cId; ConstructIdentifier cId;
// the user's response // the user's response
// has nullable string, nullable audio bytes, nullable image bytes, and timestamp // has nullable string, nullable audio bytes, nullable image bytes, and timestamp

@ -1,10 +1,9 @@
import 'package:flutter/foundation.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
import 'package:fluffychat/pangea/practice_activities/practice_record.dart'; import 'package:fluffychat/pangea/practice_activities/practice_record.dart';
import 'package:fluffychat/pangea/practice_activities/practice_record_repo.dart'; import 'package:fluffychat/pangea/practice_activities/practice_record_repo.dart';
import 'package:flutter/foundation.dart';
/// Picks which tokens to do activities on and what types of activities to do /// Picks which tokens to do activities on and what types of activities to do
/// Caches result so that we don't have to recompute it /// Caches result so that we don't have to recompute it
@ -90,7 +89,8 @@ class PracticeTarget {
} }
return tokens.every( return tokens.every(
(t) => record.responses.any((res) => res.cId == t.vocabConstructID), (t) => record.responses
.any((res) => res.cId == t.vocabConstructID && res.isCorrect),
); );
} }
} }

Loading…
Cancel
Save