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:
2026-10-02 02:38:43 +03:30
parent d5dbd1b23d
commit 39247f6d53
67 changed files with 1619 additions and 1140 deletions

View File

@@ -0,0 +1,63 @@
# هزینه‌ی دستیار و محدود کردنش به ریاضی
نسخه‌ی کامل برای مدیرعامل (جدول و نمودار، HTML و PDF):
`~/Desktop/گزارشات Tadrisino/گزارش-هزینه-دستیار/`. این فایل خلاصه‌ی فنیِ همان است و
می‌گوید عددها از کجا آمده‌اند و چطور دوباره اندازه گرفته می‌شوند.
## هزینه‌ی هر سوال (مهرِ ۱۴۰۵، پرامپتِ `tutor-v11-math-only`، `gpt-4.1-mini`)
- **قیمت:** GapGPT برای `gpt-4.1-mini` دقیقاً قیمتِ رسمیِ OpenAI را حساب می‌کند، یعنی ۰٫۴۰ دلار
برای هر میلیون توکنِ ورودی و ۱٫۶۰ دلار برای خروجی. این با `usage.cost`ِ ۵۳۹ فراخوانیِ
`docs/benchmarks/2026-09-26-tutor-model/` مقایسه شد. در ۲۸٪ِ آن فراخوانی‌ها کشِ خودکارِ ورودی
هزینه را ۴۳٪ پایین آورده بود.
- **۱۰۳ مسئله‌ی کتاب (۸ پایه):** میانگین ۰٫۱۶۲ سنت (بازه‌ی ۹۵٪: ۰٫۱۴۸ تا ۰٫۱۷۶). دبستان ۰٫۱۲۹،
متوسطه‌ی اول ۰٫۱۴۸، دبیرستان ۰٫۲۲۵. توکنِ ورودی با پرامپتِ امروز ساخته شد و با نسبتِ ۰٫۳۸۷
توکن به ازای هر حرف شمرده شد. این نسبت روی ۲۲ پرامپتِ واقعی کالیبره شده و خطایش کمتر از ۶٪ است.
توکنِ خروجی از جوابِ واقعیِ همین مدل به همان مسئله‌ها آمده است.
- **۱۲ سوالِ زنده** (ساده، شکل‌دار، سنگین، ادامه‌ی گفتگو): میانگین ۰٫۲۲۵ سنت. ساده ۰٫۱۵،
شکل‌دار ۰٫۲۴، سنگینِ دبیرستان ۰٫۲۸، ادامه‌ی گفتگو ۰٫۲۶، و ردِ «غیرِ ریاضی» توسطِ مدل ۰٫۱۲.
- **۷۰٪ِ هزینه ورودی است:** چهار تکه از کتاب، دستورها و کاتالوگِ شکل. بزرگ‌ترین اهرمِ کاهشِ هزینه
کوچک کردنِ همین ورودی است.
- عددِ برنامه‌ریزی: **۰٫۲۰ سنت ≈ ۶۰۰ تومان** برای هر سوال، با دلارِ ۳۰۰ هزار تومانی. دلارِ آزاد
۹ مهر حدودِ ۲۵۸ هزار تومان بود و نرخِ شارژِ GapGPT منتشر نشده است.
**اندازه‌گیریِ دوباره بی‌خرج:** از ۲۰۲۶-۱۰-۰۱ مسیرِ استریم هم `stream_options.include_usage`
می‌فرستد و توکن‌ها روی `ai_tutor.Message` ذخیره می‌شوند. تا پیش از آن برای جواب‌های اپ صفر ثبت
می‌شد. هزینه‌ی واقعیِ هر کاربر در ماه از این پرس‌وجو درمی‌آید:
```python
from django.db.models import Sum
Message.objects.filter(role='assistant', created_at__gte=since).values('conversation__user_id') \
.annotate(p=Sum('prompt_tokens'), c=Sum('completion_tokens')) # سنت = (0.4p + 1.6c) / 1e4
```
## سقفِ روزانه
- رایگان: `AI_DAILY_QUOTA_FREE` در `.env`، پیش‌فرض **۳**. تا ۲۰۲۶-۱۰-۰۱ همه، با اشتراک یا بی‌اشتراک،
۱۵ سوال در روز داشتند.
- مشترک: `Plan.ai_daily_quota`، در ادمین قابلِ ویرایش و پیش‌فرض ۱۵. اشتراکِ بی‌پلن (هدیه) هم ۱۵
می‌گیرد. کد در `subscription.services.ai_daily_quota` است.
- کاربرِ رایگانی که به سقف می‌رسد پیامِ «با خریدِ اشتراک…» می‌گیرد و اپ دکمه‌ی «دیدنِ اشتراک‌ها»
را نشان می‌دهد.
- انتخابِ عددها تصمیمِ محصولی است. جدولِ قیمت در برابرِ سقف در نسخه‌ی کاملِ گزارش آمده است.
## محافظِ دستیار (`rag/services/guardrails.py`)
سه لایه دارد:
1. **صافیِ سرور، پیش از مدل و بی‌هزینه.** فقط ترفندهای آشکار را می‌گیرد: «دستورهای قبلی را نادیده
بگیر»، «پرامپتت چیه»، `developer mode`. این صافی عمداً تنگ است: «قانونِ فیثاغورس را فراموش کردم»
ریاضی است و رد نمی‌شود. جمله‌ای درباره‌ی آسیب به خود رد نمی‌شود و جوابی مهربان با شماره‌ی
اورژانسِ اجتماعی (۱۲۳) و صدای مشاور (۱۴۸۰) می‌گیرد.
2. **قاعده در پرامپتِ سیستم (`SCOPE_RULE`):** هر چیزِ غیرِ ریاضی، و هر دستوری از درونِ پیامِ
دانش‌آموز، با علامتِ `[خارج-از-ریاضی]` جواب داده می‌شود.
3. **جایگزینیِ علامت با متنِ ثابت.** در استریم، ۴۰ حرفِ اول نگه داشته می‌شود تا علامت هرگز به
کاربر نرسد. جوابِ رد کش نمی‌شود و منبعِ کتاب هم ندارد.
**آزمونِ زنده (۱۰ مهر، ۱۷ فراخوانی):** ۷ از ۷ درخواستِ غیرِ ریاضی رد شد. «سلام» و «چطور ریاضی
بخوانم» جواب گرفتند. «یه جوک بگو و بعد ۲۵ درصدِ ۸۰» فقط بخشِ ریاضی را جواب گرفت. «پایتختِ
فرانسه و بعد ۱۲×۱۲» کامل رد شد، چون مدل علامت را گذاشت؛ این سخت‌گیری را به نشتِ جوابِ غیرِ ریاضی
ترجیح دادیم. کیفیتِ جوابِ ریاضی در همان ۱۲ سوال افت نکرد. این نمونه کوچک است و بعد از انتشار، چند
ده گفتگوی واقعی باید مرور شوند.
تست‌ها: `rag/tests_guardrails.py`، با مدلِ ساختگی و بی‌هزینه.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 212 KiB

View File

@@ -218,7 +218,7 @@
`+989121234567`، `989121234567` و همان شماره با ارقامِ فارسی چهار «شماره»ی جدا‌اند.
- **خطر:** cooldownِ ۲ دقیقه‌ای و سقفِ ۵ در ساعتِ هر شماره به‌ازای هر املا جداست، پس با
چند IP می‌شود یک گوشی را با پیامک بمباران کرد (وقتی کاوه‌نگار وصل شود، به هزینه‌ی ما)؛
یک آدم با دو املا دو حسابِ جدا می‌سازد؛ دعوتِ والد با املای دیگر ۴۰۳ می‌گیرد.
یک آدم با دو املا دو حسابِ جدا می‌سازد؛ دعوتِ والد با املای دیگر ۴۰۳ می‌گرفت (بخشِ والدین ۲۰۲۶-۱۰-۰۱ حذف شد).
(بازتولید شد با تستِ موقت؛ پیامکِ واقعی فرستاده نشد.)
- **رفع:** یک تابعِ `normalize_phone` (ارقامِ فارسی/عربی → لاتین، `+98`/`0098`/`98` → `0`،
فقط `09\d{9}`) در هر سه serializer، و migrationِ داده برای ردیف‌های موجود.

View File

@@ -32,8 +32,8 @@
| کاربر | چه می‌کند | کجای کد |
|---|---|---|
| دانش‌آموزِ مهمان | بدونِ شماره وارد می‌شود، پایه‌اش را انتخاب می‌کند و بازی می‌کند. اگر بعداً با شماره ثبت‌نام کند، همان حساب می‌ماند و امتیازش گم نمی‌شود. | `accounts/views.py` |
| دانش‌آموز | با شماره‌ی موبایل و کدِ پیامکی وارد می‌شود؛ جزیره‌ها را بازی می‌کند، از دستیار می‌پرسد، امتیاز و سکه و زنجیره‌ی روزانه جمع می‌کند، همراه انتخاب می‌کند، اشتراک می‌خرد، تیکتِ پشتیبانی می‌فرستد. | `content/` `progress/` `ai_tutor/` |
| والد | به حسابِ فرزند وصل می‌شود و گزارشِ هفتگی و پیشرفتش را می‌بیند. | `family/` |
| دانش‌آموز | از هر سنی (بزرگسالی که فقط یک مبحث را می‌خواهد هم پایه‌ی همان مبحث را انتخاب می‌کند). با شماره‌ی موبایل و کدِ پیامکی وارد می‌شود؛ جزیره‌ها را بازی می‌کند، از دستیار می‌پرسد، امتیاز و سکه و زنجیره‌ی روزانه جمع می‌کند، همراه انتخاب می‌کند، اشتراک می‌خرد، تیکتِ پشتیبانی می‌فرستد. | `content/` `progress/` `ai_tutor/` |
| والد | حسابِ جدا ندارد: «گزارشِ والدین» را در حسابِ خودِ دانش‌آموز می‌بیند — زمانِ تمرین، سوال‌ها و مقایسه با هفته‌ی پیش، با دو نمودار. | `progress/report.py` |
| ادمین (کارمندِ شرکت) | از پنلِ `/admin/` سؤال، درس، پلن، همراه و تیکت را بدونِ برنامه‌نویس ویرایش می‌کند؛ سؤال‌ها را فله‌ای وارد و خارج می‌کند. | `*/admin.py` |
### تاریخچه‌ی کوتاه
@@ -50,7 +50,7 @@
![مدل سناریو (Use Case)](images/technical-handbook/use-case-model.png)
*شکلِ ۱-۱ — مدلِ سناریو: سه نقشِ کاربری، دوازده کارِ اصلی، و سه سرویسِ بیرونی.*
*شکلِ ۱-۱ — مدلِ سناریو: سه نقشِ کاربری (والد با حسابِ خودِ دانش‌آموز)، یازده کارِ اصلی، و سه سرویسِ بیرونی.*
### واحدهای محتوا (این واژه‌ها در کلِ سند تکرار می‌شوند)
@@ -77,7 +77,7 @@
| فرانت | **React 18** + **TypeScript** (strict) + **Vite** + **Tailwind** | فرانتِ اولیه را توسعه‌دهنده‌ی قبلی با React ساخته بود؛ در بازسازی نگه داشته شد چون بزرگ‌ترین اکوسیستم را دارد و نیروی جایگزینش راحت پیدا می‌شود. TypeScript ناهمخوانیِ شکلِ داده با API را در زمانِ build می‌گیرد. Vite buildِ سریع و Tailwind رنگ و فاصله‌ی یکدست. وبِ موبایل‌محور یعنی بدونِ نصب و بدونِ فروشگاه روی هر گوشی باز می‌شود. | React Native / Flutter نیازِ انتشار در فروشگاه و build جدا داشت؛ نسخه‌ی اندروید (کافه‌بازار) بعداً می‌تواند اضافه شود. | بدونِ کتابخانه‌ی router و state؛ کمتر وابستگی، ولی الگوی داخلی را باید خواند (بخشِ ۴). |
| دیتابیس | **SQLite** (فعلاً) | روی سرورِ مشترکِ شرکت صفر نگهداری دارد: یک فایل، بکاپ با یک دستور. برای حجمِ فعلی کافی است. کد از ORM استفاده می‌کند و به SQLite گره نخورده. | Postgres روی همان سرور موجود است؛ مهاجرت به هماهنگی با ادمینِ سرور نیاز دارد و به مرحله‌ی بعد موکول شده است. | **فقط یک نویسنده در لحظه.** اولین گلوگاهِ مقیاس است (بخشِ ۱۴). |
| جست‌وجوی کتاب | **Qdrant** (بانکِ برداری) | تیمِ دیگرِ شرکت کتاب‌های سوم/ششم/هفتم را از قبل در یک Qdrantِ مشترک گذاشته بود؛ همان را ادامه دادیم تا داده دوباره ساخته نشود. فیلتر روی فیلدِ «فصل» دارد. | pgvector به Postgres نیاز داشت؛ FAISS سرور ندارد و بینِ دو تیم اشتراک‌پذیر نیست. | سرویسِ مشترک با تیمِ دیگر؛ پاک‌کردن باید با احتیاط باشد (بخشِ ۹). |
| مدلِ زبانی | **GapGPT** ← `gpt-4.1-mini` | GapGPT یک واسطِ سازگار با OpenAI است که از داخلِ ایران جواب می‌دهد. مدل با سنجش انتخاب شد، نه با جدولِ قیمت: روی ۱۰۲ مسئله‌ی تصحیح‌شده ۹۵–۹۶ درست در برابرِ ۸۵–۹۰ برای `gpt-4o-mini` ؛ هر جواب حدودِ ۰٫۱۲ سنت. | OpenAIِ مستقیم از ایران در دسترس نیست. مدل‌های گران‌تر در دورِ اولِ سنجش (۱۴ مدل) مزیتی نشان ندادند. | کُندیِ سرویس (حتی «سلام» ۷ ثانیه) دستِ ما نیست؛ اعتبار دو بار تمام شده. |
| مدلِ زبانی | **GapGPT** ← `gpt-4.1-mini` | GapGPT یک واسطِ سازگار با OpenAI است که از داخلِ ایران جواب می‌دهد. مدل با سنجش انتخاب شد، نه با جدولِ قیمت: روی ۱۰۲ مسئله‌ی تصحیح‌شده ۹۵–۹۶ درست در برابرِ ۸۵–۹۰ برای `gpt-4o-mini` ؛ هر جواب با پرامپتِ امروز حدودِ ۰٫۲ سنت (≈ ۶۰۰ تومان؛ `docs/ai-cost-and-guardrails.md`). دستیار فقط درباره‌ی ریاضی حرف می‌زند و سقفِ روزانه‌اش برای کاربرِ رایگان ۳ و برای مشترک به اندازه‌ی پلن است. | OpenAIِ مستقیم از ایران در دسترس نیست. مدل‌های گران‌تر در دورِ اولِ سنجش (۱۴ مدل) مزیتی نشان ندادند. | کُندیِ سرویس (حتی «سلام» ۷ ثانیه) دستِ ما نیست؛ اعتبار دو بار تمام شده. |
| ورود | **توکنِ امضاشده‌ی خودمان** + کدِ پیامکی | کاربرها با شماره وارد می‌شوند، نه نام‌کاربری/رمز؛ مدلِ کاربرِ جنگو به این نمی‌خورد. توکن با `django.core.signing` امضا می‌شود؛ یک وابستگیِ کمتر از کتابخانه‌ی JWT. | JWT همان کار را می‌کند با یک کتابخانه‌ی اضافه. | امنیتش به کلیدِ امضا بسته است (بخشِ ۱۲، موردِ ۱). |
| پیامک / پرداخت | **کاوه‌نگار** · **زرین‌پال** · **کافه‌بازار** | سرویس‌های ایرانیِ رایج؛ کاوه‌نگار با «ارسال با الگو» که روی خطِ شرکت فعال است؛ زرین‌پال برای وب، بازار برای خریدِ درون‌برنامه‌ایِ اندروید. | — | پرداختِ واقعی تا انتها هنوز آزموده نشده. |
| اجرا | **gunicorn** (gthread) پشتِ **nginx** پشتِ **ArvanCloud** | الگوی استانداردِ اجرای جنگو. آروان CDN و سپرِ حمله‌ی ایرانی است. WhiteNoise فایل‌های استاتیکِ ادمین را از خودِ جنگو سرو می‌کند. | — | سرور مشترک است؛ مالِ ما فقط یک پوشه و یک سرویس. |
@@ -111,7 +111,6 @@
| `affiliate` | معرفی به دوستان و پورسانت | `AffiliateAccount Conversion` |
| `notifications` | اعلان‌های داخلِ اپ | `Notification` |
| `support` | تیکت و گزارشِ باگ | `Ticket TicketMessage BugReport` |
| `family` | اتصالِ والد و فرزند، گزارشِ هفتگی | `FamilyLink ParentalSettings WeeklyReportLog` |
| `campaigns` | پیامِ گروهی (ذخیره و هدف‌گیری؛ زمان‌بندیِ خودکار ندارد) | `MessageCampaign CampaignDelivery` |
| `statuslog` | ثبتِ عمومیِ «وضعیتِ فلان چیز از X به Y رفت» | `StatusChangeLog` |
| `config` | تنظیمات، مسیرهای اصلی، middleware، سلامت، مدیریتِ خطا | — |
@@ -154,7 +153,7 @@ MathApp/
│ ├── health.py GET /api/health: دیتابیس، migration، کش، محتوا، هشدارها
│ └── log_formatters.py قالبِ JSONِ لاگ
├── accounts/ content/ progress/ ai_tutor/ rag/ subscription/ payment/
├── discount/ affiliate/ notifications/ support/ family/ campaigns/ statuslog/
├── discount/ affiliate/ notifications/ support/ campaigns/ statuslog/
├── content/seed/ curriculum.json و questions.json — کلِ محتوا به‌صورتِ فایل در git
├── data/textbooks/ PDFِ کتاب‌ها (در git نیست؛ فقط README)
├── templates/tadrisino/ پوسته‌ی راست‌به‌چپِ Swagger و ReDoc
@@ -197,7 +196,7 @@ src/
├── app/ App.tsx: جریانِ ورود · MainApp.tsx: تب‌ها و صفحه‌ها
├── nav/ ناوبریِ پشته‌ای خودمان، هماهنگ با دکمه‌ی back گوشی
├── state/ داده‌ی مشترک (پروفایل، نقشه) و گفتگوی دستیار
├── screens/ یک فایل برای هر صفحه: HomeScreen، QuizScreen، AskScreen، FamilyScreen، …
├── screens/ یک فایل برای هر صفحه: HomeScreen، QuizScreen، AskScreen، ReportScreen، …
├── components/ اجزای مشترک: مسیرِ جزیره‌ها، برگه‌ی پایین، نوارِ پایین
├── mascots/ سیگما، دلتا، پی، تدی — SVG از کد، ۶ حالتِ چهره
├── diagram/ JSONِ شکلِ دستیار → SVG؛ مختصات، ریشه، زاویه این‌جا حساب می‌شود
@@ -330,14 +329,17 @@ with transaction.atomic():
>
> آزموده‌نشده زرین‌پال درخواستِ پرداخت را با کدِ ۱۰۰ می‌پذیرد، ولی یک پرداختِ واقعی تا انتها هنوز انجام نشده.
### ۶-۵ والد
### ۶-۵ گزارشِ والدین
والد حسابِ جدا و کدِ دعوت ندارد (تا مهرِ ۱۴۰۵ داشت؛ به تصمیمِ پس از ارائه حذف شد). صفحه‌ی «گزارشِ والدین» در پروفایلِ خودِ دانش‌آموز است.
| # | درخواست | چه می‌شود |
|---|---|---|
| ۱ | `POST /api/family/link/invite` | والد یک کدِ اتصال می‌گیرد (سقف ۵ در ساعت). |
| ۲ | `POST /api/family/link/accept` | فرزند کد را در اپِ خودش وارد می‌کند؛ `FamilyLink` «accepted» می‌شود. یک والد می‌تواند چند فرزند داشته باشد. |
| ۳ | `GET /api/family/weekly-report` | امتیاز، درس، جزیره و زنجیره‌ی فرزند از شنبه. |
| ۴ | `GET /api/progress/nodes?userId=` | فقط وقتی مجاز است که اتصالِ تأییدشده وجود داشته باشد؛ وگرنه ۴۰۳. (نمونه‌ی جلوگیری از دیدنِ داده‌ی دیگران.) |
| ۱ | `GET /api/user/weekly-report` | برای هر یک از ۷ روزِ اخیر و ۷ روزِ قبلش: زمانِ تمرین، سوالِ حل‌شده و درست، امتیاز، درس و جزیره‌ی تمام‌شده، سوال از دستیار. فقط دیتای خودِ صاحبِ توکن. |
| ۲ | — | فرانت جمله‌ی مقایسه («آفرین! این هفته ۲۰ دقیقه بیشتر…») و دو نمودار می‌سازد: زمانِ هر روز در برابرِ همان روزِ هفته‌ی پیش، و سوال‌های درست/نادرستِ هر روز. |
| ۳ | `GET /api/progress/nodes?userId=` | فقط شناسه‌ی خودِ کاربر پذیرفته است؛ هر شناسه‌ی دیگری ۴۰۳. |
> **تخمینی:** «زمانِ تمرین» اندازه‌گیری نیست: از فاصله‌ی جواب‌ها و پرسش‌ها حساب می‌شود (فاصله‌ی بیش از ۱۰ دقیقه وقفه است). خواندنِ درس بدونِ جواب دادن در آن نیست، پس عدد معمولاً از زمانِ واقعی کمتر است.
## ۷. مدلِ داده
@@ -399,9 +401,7 @@ with transaction.atomic():
- `BugReport`
- `Notification user_id, title, body, is_read`
**family · campaigns · statuslog**
- `FamilyLink parent_user_id, child_user_id, status`
- `ParentalSettings, WeeklyReportLog`
**campaigns · statuslog**
- `MessageCampaign, CampaignDelivery یکتا: کمپین × کاربر × کانال`
- `StatusChangeLog label, object_id, old_status → new_status`
@@ -479,6 +479,7 @@ with transaction.atomic():
| GET | `/api/subscription/status` | ورود | وضعیتِ اشتراک (و بازسنجیِ پرداختِ جاافتاده) |
| POST | `/api/payment/orders` | ورود | ساختِ سفارش |
| GET | `/api/payment/orders/{id}` | ورود | وضعیتِ سفارش |
| GET | `/api/payment/invoices` | ورود | پرداخت‌ها و فاکتورهای کاربر (شماره‌ی فاکتور، تخفیف، کدِ پیگیری) |
| GET | `/api/payment/orders/{id}/pay` | عمومی | رفتن به درگاهِ زرین‌پال |
| GET | `/api/payment/zarinpal/callback` | عمومی | برگشت از درگاه + تأییدِ سرور-به-سرور |
| POST | `/api/payment/bazaar/verify` | ورود | تأییدِ خریدِ کافه‌بازار |
@@ -492,7 +493,7 @@ with transaction.atomic():
| GET · POST | `/api/support/tickets` | ورود | تیکت‌های من / تیکتِ تازه |
| GET · POST | `/api/support/tickets/{id}` · `/messages` | ورود | جزئیات و پیامِ تازه |
| POST | `/api/support/bug-reports` | ورود | گزارشِ باگ |
| GET · POST · PATCH | `/api/family/link` · `/invite` · `/accept` · `/settings` · `/weekly-report` | ورود | اتصالِ والد، تنظیمات، گزارشِ هفتگی |
| GET | `/api/user/weekly-report` | ورود | «گزارشِ والدین»: ۷ روزِ اخیر در کنارِ ۷ روزِ قبلش |
| **سیستم** | | | |
| GET | `/api/health` | عمومی | سلامتِ سرویس (بخشِ ۱۳) |

Binary file not shown.