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:
@@ -11,7 +11,7 @@ from django.shortcuts import get_object_or_404
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, extend_schema
|
||||
from rest_framework import status
|
||||
from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError
|
||||
from rest_framework.exceptions import NotFound, ValidationError
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.throttling import ScopedRateThrottle
|
||||
@@ -51,7 +51,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class GuestAwareContentThrottle(ScopedRateThrottle):
|
||||
"""
|
||||
مشترک با GuestAwareAIThrottle/GuestAwareFamilyThrottle/GuestAwareDiscountThrottle
|
||||
مشترک با GuestAwareAIThrottle/GuestAwareDiscountThrottle
|
||||
و مشابههاش: چون GuestSessionView با هر deviceId جدید یک pk تازه میسازه،
|
||||
throttleِ استانداردِ بر اساسِ user.pk با چرخشِ حسابِ مهمان دور زده میشه —
|
||||
درحالیکه answer/complete اثرِ جانبیِ ماندگار (هارت، xp/coins/progress) دارن.
|
||||
@@ -688,37 +688,24 @@ class LessonCompleteView(APIView):
|
||||
|
||||
class NodeProgressListView(APIView):
|
||||
"""
|
||||
GET /api/progress/nodes?userId=&unit= — لیست وضعیتِ کاربر روی هر Node.
|
||||
GET /api/progress/nodes?unit= — لیست وضعیتِ خودِ کاربر روی هر Node.
|
||||
|
||||
توکن اجباری است. userId قابل قبول است (والد میتواند پیشرفت فرزندش را ببیند)،
|
||||
اما فقط اگر برابر خودِ request.user باشد یا درخواستدهنده والدِ تأییدشدهی
|
||||
همان کاربر باشد (FamilyLink با status='accepted')؛ در غیر این صورت 403.
|
||||
توکن اجباری است و فقط دیتای صاحبِ توکن برمیگردد. تا ۲۰۲۶-۱۰-۰۱ پارامترِ
|
||||
userId داشت تا والدِ وصلشده پیشرفتِ فرزند را ببیند؛ بخشِ والدین از ریشه حذف
|
||||
شد و والد گزارش را در حسابِ خودِ دانشآموز میبیند (GET /api/user/weekly-report).
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(
|
||||
parameters=[
|
||||
OpenApiParameter('userId', OpenApiTypes.INT, required=False),
|
||||
OpenApiParameter('unit', OpenApiTypes.INT, required=False),
|
||||
],
|
||||
responses=NodeProgressListResponseSerializer,
|
||||
tags=['Curriculum (content)'],
|
||||
)
|
||||
def get(self, request):
|
||||
requested_user_id = request.query_params.get('userId')
|
||||
user_id = int(requested_user_id) if requested_user_id else request.user.id
|
||||
|
||||
if user_id != request.user.id:
|
||||
from family.models import FamilyLink
|
||||
|
||||
is_verified_parent = FamilyLink.objects.filter(
|
||||
parent_user_id=request.user.id, child_user_id=user_id, status='accepted',
|
||||
).exists()
|
||||
if not is_verified_parent:
|
||||
raise PermissionDenied('اجازهی مشاهدهی پیشرفتِ این کاربر را ندارید.')
|
||||
|
||||
qs = NodeProgress.objects.filter(user_id=user_id).select_related('node')
|
||||
qs = NodeProgress.objects.filter(user_id=request.user.id).select_related('node')
|
||||
unit_id = request.query_params.get('unit')
|
||||
if unit_id:
|
||||
qs = qs.filter(node__unit_id=unit_id)
|
||||
|
||||
Reference in New Issue
Block a user