senior's post-demo changes: parent report in the student's own account, Jalali birth date, invoices, math-only tutor, priced quotas
- remove the family app from the root (separate parent accounts, invite codes, parental settings); progress.0006 drops its tables, permissions and migration history, and Profile.parent_phone, the two family notification types and ?userId= on /api/progress/nodes go with it - GET /api/user/weekly-report: last 7 days next to the 7 before (estimated minutes, questions, accuracy, islands, tutor questions) for the "parent report" page - profile birthDate in Jalali (accounts/jalali.py), ages 5-99, birthYear derived from it - GET /api/payment/invoices; orders now snapshot list price and plan title - tutor answers only math: server pre-filter, scope rule in the prompt (tutor-v11), refusal marker replaced with fixed text; self-harm gets a help line instead - streamed answers now record token usage (it was always 0) - daily tutor quota: AI_DAILY_QUOTA_FREE (default 3) vs Plan.ai_daily_quota - delete_users management command for wiping test accounts across every app - docs/ai-cost-and-guardrails.md: measured cost per question and guardrail results Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -529,6 +529,22 @@ class SetUserGradeView(APIView):
|
||||
return Response(SetGradeResponseSerializer(data).data)
|
||||
|
||||
|
||||
def _profile_payload(user, profile) -> dict:
|
||||
from . import jalali
|
||||
|
||||
return {
|
||||
'userId': str(user.id),
|
||||
'phone': user.phone,
|
||||
'name': user.name,
|
||||
'avatar': user.avatar,
|
||||
'isGuest': user.is_guest,
|
||||
'gradeId': user.grade_id,
|
||||
'birthYear': profile.birth_year,
|
||||
'birthDate': jalali.to_string(profile.birth_date) if profile.birth_date else None,
|
||||
'bio': profile.bio,
|
||||
}
|
||||
|
||||
|
||||
class UserProfileView(APIView):
|
||||
"""
|
||||
GET /api/user/profile — اطلاعات پروفایل کاربر جاری
|
||||
@@ -562,18 +578,7 @@ class UserProfileView(APIView):
|
||||
# زده میشه (OneToOne روی user_id جاری، نه ورودیِ خارجی).
|
||||
profile, _ = Profile.objects.get_or_create(user_id=user.id)
|
||||
|
||||
data = {
|
||||
'userId': str(user.id),
|
||||
'phone': user.phone,
|
||||
'name': user.name,
|
||||
'avatar': user.avatar,
|
||||
'isGuest': user.is_guest,
|
||||
'gradeId': user.grade_id,
|
||||
'birthYear': profile.birth_year,
|
||||
'parentPhone': profile.parent_phone,
|
||||
'bio': profile.bio,
|
||||
}
|
||||
return Response(ProfileResponseSerializer(data).data)
|
||||
return Response(ProfileResponseSerializer(_profile_payload(user, profile)).data)
|
||||
|
||||
@extend_schema(
|
||||
request=UpdateProfileRequestSerializer,
|
||||
@@ -589,7 +594,7 @@ class UserProfileView(APIView):
|
||||
profile, _ = Profile.objects.get_or_create(user_id=user.id)
|
||||
|
||||
user_fields = {'name': 'name', 'avatar': 'avatar'}
|
||||
profile_fields = {'birthYear': 'birth_year', 'parentPhone': 'parent_phone', 'bio': 'bio'}
|
||||
profile_fields = {'birthYear': 'birth_year', 'birthDate': 'birth_date', 'bio': 'bio'}
|
||||
|
||||
changed_user = [attr for key, attr in user_fields.items() if key in v]
|
||||
changed_profile = [attr for key, attr in profile_fields.items() if key in v]
|
||||
@@ -600,6 +605,17 @@ class UserProfileView(APIView):
|
||||
for key, attr in profile_fields.items():
|
||||
if key in v:
|
||||
setattr(profile, attr, v[key])
|
||||
# سال و تاریخ نباید از هم جدا بیفتند: تاریخ سال را میسازد، و سالِ تنها
|
||||
# (اپِ قدیمی) تاریخی را که دیگر با آن نمیخواند پاک میکند.
|
||||
if 'birthDate' in v:
|
||||
from . import jalali
|
||||
profile.birth_year = jalali.from_gregorian(v['birthDate'])[0] if v['birthDate'] else None
|
||||
changed_profile.append('birth_year')
|
||||
elif 'birthYear' in v and profile.birth_date is not None:
|
||||
from . import jalali
|
||||
if jalali.from_gregorian(profile.birth_date)[0] != v['birthYear']:
|
||||
profile.birth_date = None
|
||||
changed_profile.append('birth_date')
|
||||
|
||||
# update_fields: فقط همان ستونهایی که واقعاً عوض شدهاند نوشته شوند تا
|
||||
# PATCH همزمان از یک دستگاهِ دیگر، فیلدهای دستنخورده را بازنویسی نکند.
|
||||
@@ -608,15 +624,4 @@ class UserProfileView(APIView):
|
||||
if changed_profile:
|
||||
profile.save(update_fields=changed_profile)
|
||||
|
||||
data = {
|
||||
'userId': str(user.id),
|
||||
'phone': user.phone,
|
||||
'name': user.name,
|
||||
'avatar': user.avatar,
|
||||
'isGuest': user.is_guest,
|
||||
'gradeId': user.grade_id,
|
||||
'birthYear': profile.birth_year,
|
||||
'parentPhone': profile.parent_phone,
|
||||
'bio': profile.bio,
|
||||
}
|
||||
return Response(ProfileResponseSerializer(data).data)
|
||||
return Response(ProfileResponseSerializer(_profile_payload(user, profile)).data)
|
||||
|
||||
Reference in New Issue
Block a user