Files
MathApp/config/urls.py
Zareian 39247f6d53 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>
2026-10-02 02:38:43 +03:30

93 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
مسیرهای اصلی پروژه‌ی Tadrisino — فاز ۱ (۴ از ۱۱ اپ)
هر اپ زیر پیشوند /api/ خودش mount شده. مسیرهای هر endpoint دقیقاً طبق
سند «Endpointهای API» طراحی‌شده تا مقایسه با Swagger راحت باشه.
"""
import logging
from django.contrib import admin
from django.http import JsonResponse
from django.urls import include, path
from django.views import defaults as django_default_views
from drf_spectacular.utils import extend_schema
from config.health import HealthView
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularRedocView,
SpectacularSwaggerView,
)
@extend_schema(exclude=True)
class HealthViewSlashAlias(HealthView):
"""نسخه‌ی اسلش‌دارِ `/api/health`، فقط برای پایشِ بیرونی — بیرون از اسکیما."""
logger = logging.getLogger(__name__)
def handler404(request, exception=None):
"""برای مسیرهای /api/ همیشه JSON برمی‌گردونه؛ بقیه رفتارِ پیش‌فرضِ جنگو."""
if request.path.startswith('/api/'):
return JsonResponse({'detail': 'یافت نشد.'}, status=404)
return django_default_views.page_not_found(request, exception)
def handler500(request):
"""شبکه‌ی ایمنیِ نهایی برای exceptionی که حتی به config/exceptions.py هم
نرسیده (مثلاً در خودِ middleware/URL resolution). traceback کامل رو
config/middleware.py قبلاً لاگ کرده؛ اینجا فقط یه پاسخِ JSON می‌ده."""
logger.error('unhandled_500 path=%s', request.path)
if request.path.startswith('/api/'):
return JsonResponse(
{'detail': 'خطای غیرمنتظره‌ای رخ داد؛ لطفاً بعداً دوباره تلاش کنید.'}, status=500,
)
return django_default_views.server_error(request)
urlpatterns = [
path('admin/', admin.site.urls),
# بررسیِ سلامتِ نسخه‌ی مستقرشده — بدونِ احراز، برای پایشِ بیرونی.
#
# هر دو شکل ثبت می‌شود، با و بدونِ اسلشِ آخر. دلیلش تجربه‌ی واقعی است:
# پایشِ سرور `/api/health/` صدا می‌زد و چون فقط شکلِ بی‌اسلش ثبت بود،
# کسی روی سرور همین فایل را دستی ویرایش کرد تا کار کند — و از آن لحظه
# هر `git pull` بعدی روی همین فایل گیر می‌کرد. یک مسیرِ اضافه ارزانش
# است در برابرِ یک فایلِ دست‌کاری‌شده روی پروداکشن.
path('api/health', HealthView.as_view(), name='health'),
# همان ویو، فقط برای پایشی که با اسلش صدا می‌زند. از اسکیما بیرون است تا
# دو مسیرِ یکسان operationIdهای health_retrieve و health_retrieve_2 نسازند.
#
# ⚠️ زیرکلاس لازم است، نه exclude روی خودِ HealthView. extend_schema روی
# یک کلاس همان کلاس را تغییر می‌دهد، و چون هر دو مسیر به یک کلاس اشاره
# می‌کردند، `/api/health` هم با خودش از اسکیما بیرون می‌افتاد — یعنی
# اندپوینتِ سلامت اصلاً در Swagger دیده نمی‌شد.
path('api/health/', HealthViewSlashAlias.as_view(), name='health-slash'),
# OpenAPI schema + Swagger UI + Redoc
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
# قالبِ خودمان فقط یک پوسته‌ی راست‌به‌چپ روی قالبِ drf-spectacular است؛
# بدونش متنِ فارسی در یک بلوکِ LTR رندر می‌شود و نقطه‌ی جمله و ترتیبِ
# ستون‌های جدول جابه‌جا می‌شوند. توضیح در خودِ قالب.
path('api/docs/', SpectacularSwaggerView.as_view(
url_name='schema', template_name='tadrisino/swagger_ui.html'), name='swagger-ui'),
path('api/redoc/', SpectacularRedocView.as_view(
url_name='schema', template_name='tadrisino/redoc.html'), name='redoc'),
# همه زیرِ /api/ جمع می‌شوند؛ هر اپ مسیرهای خودش را نگه می‌دارد، پس
# ترتیبِ این فهرست اهمیتی ندارد و تداخلی بینشان نیست.
path('api/', include('accounts.urls')),
path('api/', include('content.urls')),
path('api/', include('progress.urls')),
path('api/', include('ai_tutor.urls')),
path('api/', include('rag.urls')),
path('api/', include('subscription.urls')),
path('api/', include('payment.urls')),
path('api/', include('discount.urls')),
path('api/', include('notifications.urls')),
path('api/', include('affiliate.urls')),
path('api/', include('support.urls')),
]