diff --git a/bot.py b/bot.py index 80711ad..5faf6b5 100644 --- a/bot.py +++ b/bot.py @@ -9254,6 +9254,16 @@ def build_block_summary_keyboard(user_data: dict) -> InlineKeyboardMarkup: translate("block_retry_errors", locale), callback_data=f"bretry:{session_id}", ) + elif user_data.get("block_mode") == "flash": + primary = InlineKeyboardButton( + translate("block_reinforce_quiz", locale), + callback_data=f"bmode:{session_id}:quiz", + ) + elif user_data.get("block_mode") == "quiz": + primary = InlineKeyboardButton( + translate("block_reinforce_written", locale), + callback_data=f"bmode:{session_id}:type", + ) else: primary = InlineKeyboardButton( translate("block_another_lesson", locale), diff --git a/mydictionary/localization.py b/mydictionary/localization.py index e2295fe..1898f87 100644 --- a/mydictionary/localization.py +++ b/mydictionary/localization.py @@ -979,6 +979,8 @@ "block_intro": "📖 *{topic}*\nMemorize {count} words:\n\n{study}", "block_quiz_mode": "Quiz · 4 choices", "block_written_mode": "Written", + "block_reinforce_quiz": "🧠 Reinforce with a quiz", + "block_reinforce_written": "✍️ Answer in writing", "block_ai_tutor": "AI tutor", "block_voice_practice": "🎤 Pronounce 10 words", "block_topics_study": "Topics 📚", @@ -1005,6 +1007,8 @@ "block_intro": "📖 *{topic}*\nMémorisez {count} mots :\n\n{study}", "block_quiz_mode": "Quiz · 4 choix", "block_written_mode": "Par écrit", + "block_reinforce_quiz": "🧠 Consolider avec un quiz", + "block_reinforce_written": "✍️ Répondre par écrit", "block_ai_tutor": "Tuteur IA", "block_voice_practice": "🎤 Prononcer 10 mots", "block_topics_study": "Thèmes 📚", @@ -1031,6 +1035,8 @@ "block_intro": "📖 *{topic}*\nPräge dir {count} Wörter ein:\n\n{study}", "block_quiz_mode": "Quiz · 4 Antworten", "block_written_mode": "Schriftlich", + "block_reinforce_quiz": "🧠 Mit einem Quiz festigen", + "block_reinforce_written": "✍️ Schriftlich antworten", "block_ai_tutor": "KI-Tutor", "block_voice_practice": "🎤 10 Wörter aussprechen", "block_topics_study": "Themen 📚", @@ -1057,6 +1063,8 @@ "block_intro": "📖 *{topic}*\n{count}語を覚えましょう:\n\n{study}", "block_quiz_mode": "クイズ · 4択", "block_written_mode": "入力", + "block_reinforce_quiz": "🧠 クイズで定着", + "block_reinforce_written": "✍️ 入力して答える", "block_ai_tutor": "AIチューター", "block_voice_practice": "🎤 10語を発音", "block_topics_study": "トピック 📚", @@ -1083,6 +1091,8 @@ "block_intro": "📖 *{topic}*\nاحفظ {count} كلمات:\n\n{study}", "block_quiz_mode": "اختبار · 4 خيارات", "block_written_mode": "كتابة", + "block_reinforce_quiz": "🧠 ثبّت التعلم باختبار", + "block_reinforce_written": "✍️ أجب كتابةً", "block_ai_tutor": "معلّم AI", "block_voice_practice": "🎤 انطق 10 كلمات", "block_topics_study": "الموضوعات 📚", @@ -1109,6 +1119,8 @@ "block_intro": "📖 *{topic}*\n请记住 {count} 个单词:\n\n{study}", "block_quiz_mode": "测验 · 4 个选项", "block_written_mode": "书写", + "block_reinforce_quiz": "🧠 用测验巩固", + "block_reinforce_written": "✍️ 书面作答", "block_ai_tutor": "AI 导师", "block_voice_practice": "🎤 朗读 10 个单词", "block_topics_study": "主题 📚", @@ -1135,6 +1147,8 @@ "block_intro": "📖 *{topic}*\nЗапомни {count} слов:\n\n{study}", "block_quiz_mode": "Тест · 4 варианта", "block_written_mode": "Письменно", + "block_reinforce_quiz": "🧠 Закрепить тестом", + "block_reinforce_written": "✍️ Ответить письменно", "block_ai_tutor": "AI-репетитор", "block_voice_practice": "🎤 Произнести 10 слов", "block_topics_study": "Темы 📚", @@ -1161,6 +1175,8 @@ "block_intro": "📖 *{topic}*\nMemoriza {count} palabras:\n\n{study}", "block_quiz_mode": "Test · 4 opciones", "block_written_mode": "Por escrito", + "block_reinforce_quiz": "🧠 Reforzar con un test", + "block_reinforce_written": "✍️ Responder por escrito", "block_ai_tutor": "Tutor de IA", "block_voice_practice": "🎤 Pronunciar 10 palabras", "block_topics_study": "Temas 📚", diff --git a/tests/test_post_lesson_reinforcement_v1.py b/tests/test_post_lesson_reinforcement_v1.py new file mode 100644 index 0000000..200befc --- /dev/null +++ b/tests/test_post_lesson_reinforcement_v1.py @@ -0,0 +1,89 @@ +"""Post-lesson reinforcement keeps learners moving without another chooser.""" + +import os +import unittest + +os.environ.setdefault("BOT_TOKEN", "123456:TESTTOKEN_ABCDEFGHIJKLMNOP") +os.environ.setdefault("ALLOWED_USER_ID", "1") +os.environ.setdefault("ALLOW_SQLITE_DEV", "true") + +import bot +from mydictionary.localization import INTERFACE_LOCALES, translate + + +def buttons(markup): + return [button for row in markup.inline_keyboard for button in row] + + +class PostLessonReinforcementTest(unittest.TestCase): + def completed(self, mode: str, *, wrong: list[int] | None = None, locale: str = "en"): + state = {"interface_locale": locale} + bot.reset_block_state( + state, + [0, 1, 2], + "ja", + None, + "ja-basics-100", + lesson_kind="daily", + ) + bot.start_block_attempt(state, mode) + mistakes = list(wrong or []) + state.update( + block_pos=3, + block_correct=3 - len(mistakes), + block_wrong=mistakes, + ) + return state + + def test_perfect_cards_offer_quiz_on_the_same_words(self): + state = self.completed("flash") + + inline = buttons(bot.build_block_summary_keyboard(state)) + + self.assertLessEqual(len(inline), 3) + self.assertEqual( + inline[0].callback_data, + f"bmode:{state['block_session']}:quiz", + ) + self.assertEqual(inline[0].text, translate("block_reinforce_quiz", "en")) + + def test_perfect_quiz_offers_written_recall_on_the_same_words(self): + state = self.completed("quiz", locale="ru") + + inline = buttons(bot.build_block_summary_keyboard(state)) + + self.assertLessEqual(len(inline), 3) + self.assertEqual( + inline[0].callback_data, + f"bmode:{state['block_session']}:type", + ) + self.assertEqual(inline[0].text, translate("block_reinforce_written", "ru")) + + def test_perfect_written_practice_still_offers_another_lesson(self): + state = self.completed("type") + + inline = buttons(bot.build_block_summary_keyboard(state)) + + self.assertEqual(inline[0].callback_data, "start:daily") + self.assertEqual(inline[0].text, translate("block_another_lesson", "en")) + + def test_errors_keep_retry_first_in_every_mode(self): + for mode in ("flash", "quiz", "type"): + with self.subTest(mode=mode): + state = self.completed(mode, wrong=[1]) + inline = buttons(bot.build_block_summary_keyboard(state)) + self.assertLessEqual(len(inline), 3) + self.assertEqual( + inline[0].callback_data, + f"bretry:{state['block_session']}", + ) + + def test_reinforcement_copy_exists_for_every_interface_locale(self): + for locale in INTERFACE_LOCALES: + with self.subTest(locale=locale): + self.assertTrue(translate("block_reinforce_quiz", locale).strip()) + self.assertTrue(translate("block_reinforce_written", locale).strip()) + + +if __name__ == "__main__": + unittest.main()