- content: POST nodes/{id}/quiz/complete and lessons/{id}/complete refuse a locked island
(403 node_locked, same rule as /api/home) and a user who has not answered every question
(409 answers_missing). Before, only a user with zero hearts and no answers was refused, so
anyone else finished any island, locked or not, and finishing a locked island's lessons
unlocked it. A node or lesson with no questions still passes when unlocked, with no XP.
The client can no longer understate totalQuestions to buy stars.
- throttle_scope on 15 routes that had none (question bank without login, intro, hearts,
notifications, tickets, plans, subscription status, orders, offers, affiliate, assistant
history and feedback, logout).
- admin: OTPCode and Session can only be added/changed by a superuser.
- deploy.sh: integrity-checked DB backup before migrate (keeps the last 2); no migrate without it.
- docs and CLAUDE.md updated to say what is verified and what is not.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
28 lines
1.6 KiB
Python
28 lines
1.6 KiB
Python
"""کمکتابعِ تستها: جوابِ ثبتشده مثلِ اپ (اپ هر سوال را جدا با /answer ثبت میکند و بعد پایان را میزند)."""
|
||
from .models import Choice, Question, UserAnswer
|
||
|
||
|
||
def record_answers(user_id, *, lesson=None, node=None, correct=True):
|
||
"""
|
||
به همهی سوالهای یک درس یا یک جزیره جوابِ ثبتشده میدهد (مستقیم در DB، بیقلب و بیthrottle).
|
||
`correct=True` گزینهی درست، `False` گزینهی غلط را میزند؛ سوالی که آن گزینه را ندارد
|
||
گزینهاش ساخته میشود. سوالی که پیشتر جواب داده شده دست نمیخورد. تعدادِ سوالهای جوابِدادهشده را برمیگرداند.
|
||
"""
|
||
questions = Question.objects.all()
|
||
if lesson is not None:
|
||
questions = questions.filter(lesson=lesson)
|
||
if node is not None:
|
||
questions = questions.filter(lesson__node=node)
|
||
count = 0
|
||
for question in questions.prefetch_related('choices'):
|
||
choice = next((c for c in question.choices.all() if c.is_correct == correct), None)
|
||
if choice is None:
|
||
choice = Choice.objects.create(
|
||
question=question, value='درست' if correct else 'غلط', is_correct=correct)
|
||
UserAnswer.objects.get_or_create(
|
||
user_id=user_id, question=question,
|
||
defaults={'choice': choice, 'correct': correct, 'xp_earned': 10 if correct else 0},
|
||
)
|
||
count += 1
|
||
return count
|