diff --git a/lib/pangea/choreographer/controllers/alternative_translator.dart b/lib/pangea/choreographer/controllers/alternative_translator.dart index 1e69f1e1e..def852fcd 100644 --- a/lib/pangea/choreographer/controllers/alternative_translator.dart +++ b/lib/pangea/choreographer/controllers/alternative_translator.dart @@ -5,10 +5,11 @@ import 'package:http/http.dart' as http; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart'; -import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import '../repo/similarity_repo.dart'; +//import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart'; + class AlternativeTranslator { final Choreographer choreographer; bool showAlternativeTranslations = false; @@ -31,6 +32,20 @@ class AlternativeTranslator { similarityResponse = null; } + double get _percentCorrectChoices { + final attemptTracker = choreographer.itController.attemptTracker; + + int correctFirstAttempts = 0; + for (final entry in attemptTracker.entries) { + if (entry.value) correctFirstAttempts++; + } + + final int totalSteps = attemptTracker.length; + final double percentage = (correctFirstAttempts / totalSteps) * 100; + + return percentage; + } + Future setTranslationFeedback() async { try { choreographer.startLoading(); @@ -40,62 +55,17 @@ class AlternativeTranslator { userTranslation = choreographer.currentText; - if (choreographer.itController.allCorrect) { - translationFeedbackKey = FeedbackKey.allCorrect; - return; - } + // Calculate percentage based on correct/total choices ratio + final double percentCorrect = _percentCorrectChoices; - final String? goldRouteTranslation = - choreographer.itController.goldRouteTracker.fullTranslation; - - final FullTextTranslationResponseModel results = - await FullTextTranslationRepo.translate( - accessToken: choreographer.accessToken, - request: FullTextTranslationRequestModel( - text: choreographer.itController.sourceText!, - tgtLang: choreographer.l2LangCode!, - userL2: choreographer.l2LangCode!, - userL1: choreographer.l1LangCode!, - deepL: goldRouteTranslation == null, - ), - ); - translations = results.translations; - if (results.deepL != null || goldRouteTranslation != null) { - translations.insert(0, (results.deepL ?? goldRouteTranslation)!); - } - // final List altAndUser = [...results.translations]; - // if (results.deepL != null) { - // altAndUser.add(results.deepL!); - // } - // altAndUser.add(userTranslation); - - if (userTranslation?.toLowerCase() == - results.bestTranslation.toLowerCase()) { + // Set feedback based on percentage + if (percentCorrect == 100) { translationFeedbackKey = FeedbackKey.allCorrect; - return; + } else if (percentCorrect > 91) { + translationFeedbackKey = FeedbackKey.newWayAllGood; + } else { + translationFeedbackKey = FeedbackKey.othersAreBetter; } - - similarityResponse = await SimilarityRepo.get( - accessToken: choreographer.accessToken, - request: SimilarityRequestModel( - benchmark: results.bestTranslation, - toCompare: [userTranslation!], - ), - ); - - // if (similarityResponse! - // .userTranslationIsSameAsBotTranslation(userTranslation!)) { - // translationFeedbackKey = FeedbackKey.allCorrect; - // return; - // } - - // if (similarityResponse! - // .userTranslationIsDifferentButBetter(userTranslation!)) { - // translationFeedbackKey = FeedbackKey.newWayAllGood; - // return; - // } - showAlternativeTranslations = true; - translationFeedbackKey = FeedbackKey.othersAreBetter; } catch (err, stack) { if (err is! http.Response) { ErrorHandler.logError( @@ -120,29 +90,22 @@ class AlternativeTranslator { } String translationFeedback(BuildContext context) { + final String displayScore = _percentCorrectChoices.toStringAsFixed(0); + + // Use original feedback messages switch (translationFeedbackKey) { case FeedbackKey.allCorrect: - return "Match: 100%\n${L10n.of(context).allCorrect}"; + return "Match: $displayScore%\n${L10n.of(context).allCorrect}"; case FeedbackKey.newWayAllGood: - return "Match: 100%\n${L10n.of(context).newWayAllGood}"; + return "Match: $displayScore%\n${L10n.of(context).newWayAllGood}"; case FeedbackKey.othersAreBetter: - final num userScore = - (similarityResponse!.userScore(userTranslation!) * 100).round(); - final String displayScore = userScore.toString(); - if (userScore > 90) { + if (_percentCorrectChoices > 90) { return "Match: $displayScore%\n${L10n.of(context).almostPerfect}"; } - if (userScore > 80) { + if (_percentCorrectChoices > 70) { return "Match: $displayScore%\n${L10n.of(context).prettyGood}"; } return "Match: $displayScore%\n${L10n.of(context).othersAreBetter}"; - // case FeedbackKey.commonalityFeedback: - // final int count = controller.completedITSteps - // .where((element) => element.isCorrect) - // .toList() - // .length; - // final int total = controller.completedITSteps.length; - // return L10n.of(context).commonalityFeedback(count,total); case FeedbackKey.loadingPleaseWait: return L10n.of(context).letMeThink; case FeedbackKey.allDone: diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index d52961316..8fb5827cb 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -348,6 +348,21 @@ class Choreographer { } } + void clear() { + choreoMode = ChoreoMode.igc; + _lastChecked = null; + _timesClicked = 0; + isFetching = false; + choreoRecord = ChoreoRecord.newRecord; + itController.clear(); + igc.dispose(); + _resetDebounceTimer(); + + final savedTracker = Map.from(itController.attemptTracker); + itController.clear(); + itController.attemptTracker = savedTracker; + } + void onITChoiceSelect(ITStep step) { choreoRecord.addRecord(_textController.text, step: step); _textController.setSystemText( @@ -516,9 +531,6 @@ class Choreographer { } void onSelectAlternativeTranslation(String translation) { - // PTODO - add some kind of record of this - // choreoRecord.addRecord(_textController.text, match); - _textController.setSystemText( translation, EditType.alternativeTranslation, @@ -542,19 +554,6 @@ class Choreographer { String get accessToken => pangeaController.userController.accessToken; - clear() { - choreoMode = ChoreoMode.igc; - _lastChecked = null; - _timesClicked = 0; - isFetching = false; - choreoRecord = ChoreoRecord.newRecord; - itController.clear(); - igc.dispose(); - //@ggurdin - why is this commented out? - // errorService.clear(); - _resetDebounceTimer(); - } - void onMatchError({int? cursorOffset}) { if (cursorOffset == null) { igc.igcTextData?.matches.clear(); diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index 6e6580f36..41a40bbd0 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -54,6 +54,9 @@ class ITController { goldRouteTracker = GoldRouteTracker.defaultTracker; payLoadIds = []; + attemptTracker.clear(); + visitedSteps.clear(); + choreographer.altTranslator.clear(); choreographer.choreoMode = ChoreoMode.igc; choreographer.setState(); @@ -312,42 +315,66 @@ class ITController { // ) // : null; + Map attemptTracker = {}; + Set visitedSteps = {}; + //maybe we store IT data in the same format? make a specific kind of match? void selectTranslation(int chosenIndex) { if (currentITStep == null) return; - final itStep = ITStep(currentITStep!.continuances, chosen: chosenIndex); - completedITSteps.add(itStep); + // Check if this answer is correct + final bool isCorrect = currentITStep!.continuances[chosenIndex].gold || + currentITStep!.continuances[chosenIndex].level == + ChoreoConstants.levelThresholdForGreen; + + // Only proceed if the answer was correct + if (isCorrect) { + final itStep = ITStep(currentITStep!.continuances, chosen: chosenIndex); + completedITSteps.add(itStep); + + showChoiceFeedback = true; + + final List? ignoredTokens = currentITStep?.continuances + .where((e) => !e.wasClicked) + .map((e) => e.tokens) + .expand((e) => e) + .toList(); + + choreographer.pangeaController.putAnalytics.addDraftUses( + ignoredTokens ?? [], + choreographer.roomId, + ConstructUseTypeEnum.ignIt, + AnalyticsUpdateOrigin.it, + ); - showChoiceFeedback = true; + Future.delayed( + const Duration( + milliseconds: ChoreoConstants.millisecondsToDisplayFeedback, + ), + () { + showChoiceFeedback = false; + choreographer.setState(); + }, + ); - // Get a list of the choices that the user did not click - final List? ignoredTokens = currentITStep?.continuances - .where((e) => !e.wasClicked) - .map((e) => e.tokens) - .expand((e) => e) - .toList(); + choreographer.onITChoiceSelect(itStep); + } else { + choreographer.setState(); + } + } - // Save those choices' tokens to local construct analytics as ignored tokens - choreographer.pangeaController.putAnalytics.addDraftUses( - ignoredTokens ?? [], - choreographer.roomId, - ConstructUseTypeEnum.ignIt, - AnalyticsUpdateOrigin.it, - ); + double getFirstAttemptPercentage() { + if (attemptTracker.isEmpty) { + return 0; + } - Future.delayed( - const Duration( - milliseconds: ChoreoConstants.millisecondsToDisplayFeedback, - ), - () { - showChoiceFeedback = false; - choreographer.setState(); - }, - ); + final int correctFirstAttempts = + attemptTracker.values.where((correct) => correct).length; + final int totalSteps = attemptTracker.length; - choreographer.onITChoiceSelect(itStep); - choreographer.setState(); + final double percentage = (correctFirstAttempts / totalSteps) * 100; + + return percentage; } String get uniqueKeyForLayerLink => "itChoices${choreographer.roomId}"; diff --git a/lib/pangea/choreographer/widgets/it_bar.dart b/lib/pangea/choreographer/widgets/it_bar.dart index 537d829f2..9638bc5ec 100644 --- a/lib/pangea/choreographer/widgets/it_bar.dart +++ b/lib/pangea/choreographer/widgets/it_bar.dart @@ -370,6 +370,16 @@ class ITChoices extends StatelessWidget { void selectContinuance(int index, BuildContext context) { final Continuance continuance = controller.currentITStep!.continuances[index]; + + final int currentStepIndex = controller.completedITSteps.length; + + final bool isCorrectChoice = (continuance.level == 1); + + // Record first attempt for this step if not already recorded + if (!controller.attemptTracker.containsKey(currentStepIndex)) { + controller.attemptTracker[currentStepIndex] = isCorrectChoice; + } + if (continuance.level == 1) { Future.delayed( const Duration(milliseconds: 500), @@ -383,6 +393,7 @@ class ITChoices extends StatelessWidget { continuance.feedbackText(context), ); } + if (!continuance.wasClicked) { controller.choreographer.pangeaController.putAnalytics.addDraftUses( continuance.tokens, @@ -393,7 +404,8 @@ class ITChoices extends StatelessWidget { AnalyticsUpdateOrigin.it, ); } - controller.currentITStep!.continuances[index].wasClicked = true; + + continuance.wasClicked = true; controller.choreographer.setState(); }