return mascotMood on answer submission
The math-app frontend prototype drives a RobotMascot with a fixed set of
moods; this gives that animation an explicit backend signal. Complements
the existing Lesson.mascot_animation / LessonIntro.mascotAnimation, which
are unchanged.
serializers.py
- AnswerResponseSerializer: new mascotMood field.
views.py
- QuestionAnswerView: mascotMood = 'happy' for a correct answer else
'encourage', on both the fresh response and the idempotent-replay
response.
tests.py
- QuestionAnswerMascotMoodTests: correct -> happy, wrong -> encourage,
and the mood is preserved across an idempotent replay.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -490,3 +490,54 @@ class LessonCompleteGuestThrottleBypassTests(TestCase):
|
||||
self.assertEqual(UserLessonCompletion.objects.filter(user_id=guest2.id).count(), 0)
|
||||
self.assertFalse(UserStats.objects.filter(user_id=guest2.id).exists())
|
||||
self.assertFalse(NodeProgress.objects.filter(user_id=guest2.id, node=self.node).exists())
|
||||
|
||||
|
||||
class QuestionAnswerMascotMoodTests(TestCase):
|
||||
"""پاسخِ /answer باید mascotMood بده: درست → happy، غلط → encourage
|
||||
(همخوان با RobotMood در MathApp_frontend)."""
|
||||
|
||||
def setUp(self):
|
||||
from django.core.cache import cache
|
||||
cache.clear()
|
||||
self.client = APIClient()
|
||||
self.user = User.objects.create(device_id='device-mascot-1', is_guest=True, name='')
|
||||
self.token = issue_access_token(self.user.id)
|
||||
grade = Grade.objects.create(title='پایه اول', icon='i', color='#fff')
|
||||
unit = Unit.objects.create(grade=grade, title='فصل ۱', order=1)
|
||||
node = Node.objects.create(unit=unit, title='گره ۱', order=1)
|
||||
lesson = Lesson.objects.create(node=node, title='درس ۱', intro_text='...')
|
||||
self.question = Question.objects.create(lesson=lesson, type='multiple_choice', prompt='۱+۱؟')
|
||||
self.correct = Choice.objects.create(question=self.question, value='۲', is_correct=True)
|
||||
self.wrong = Choice.objects.create(question=self.question, value='۳', is_correct=False)
|
||||
|
||||
def _answer(self, choice_id):
|
||||
return self.client.post(
|
||||
f'/api/questions/{self.question.id}/answer',
|
||||
{'selectedOptionId': str(choice_id)}, format='json',
|
||||
HTTP_AUTHORIZATION=f'Bearer {self.token}',
|
||||
)
|
||||
|
||||
def test_correct_answer_mood_happy(self):
|
||||
r = self._answer(self.correct.id)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.data['mascotMood'], 'happy')
|
||||
|
||||
def test_wrong_answer_mood_encourage(self):
|
||||
r = self._answer(self.wrong.id)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(r.data['mascotMood'], 'encourage')
|
||||
|
||||
def test_idempotent_replay_keeps_mood(self):
|
||||
k = 'mascot-idem-1'
|
||||
first = self.client.post(
|
||||
f'/api/questions/{self.question.id}/answer',
|
||||
{'selectedOptionId': str(self.wrong.id)}, format='json',
|
||||
HTTP_AUTHORIZATION=f'Bearer {self.token}', HTTP_IDEMPOTENCY_KEY=k,
|
||||
)
|
||||
replay = self.client.post(
|
||||
f'/api/questions/{self.question.id}/answer',
|
||||
{'selectedOptionId': str(self.wrong.id)}, format='json',
|
||||
HTTP_AUTHORIZATION=f'Bearer {self.token}', HTTP_IDEMPOTENCY_KEY=k,
|
||||
)
|
||||
self.assertEqual(first.data['mascotMood'], 'encourage')
|
||||
self.assertEqual(replay.data['mascotMood'], 'encourage')
|
||||
|
||||
Reference in New Issue
Block a user