Skip to Content

التشخيص والتدريب

تشكّل نظاما التشخيص والتدريب حلقة القياس التي يمر بها الطالب. تجمع جلسة التشخيص الإجابات المصنّفة وتمررها إلى محرك الحساب الذي ينتج درجة θ (مثبّتة بالمعايرة) ومعيار الخطأ المرافق لها، ثم يُغذّي هذا الناتج محرك حالة المهارة وصولاً إلى ملفات الطلاب وحزم التدخل. يأتي التدريب بعد ذلك ليدير حلقة تكيّفية مبنية على نتائج التشخيص المكتمل.

يقع كلا النظامين في modules/session، بينما يقع محرك الحساب في modules/engine.

جلسة التشخيص

دورة الحياة

جلسة التشخيص (صفّ في student_session نوعه diagnostic، مع تفاصيله في student_session_diagnostic) هي الحاوية المتغيّرة التي تتابع جلوس الطالب. قوائم التدريب ومجموعات التأكيد هما النوعان الآخران في الجدول نفسه. لا يمكن أن تكون هناك أكثر من جلسة مفتوحة واحدة لكل طالب في كل دورة تقييم؛ يؤدي محاولة فتح جلسة ثانية إلى إرجاع 409 مع معرّف الجلسة القائمة.

تمر الجلسة بأربع حالات:

active → paused → complete ↓ abandoned

عند الوصول إلى حد 15 دقيقة من الوقت الفعّال، تُغلق الجلسة بـsessionEndReason: time_cap؛ تصبح الحالة complete (لا توجد حالة time_capped مستقلة). يؤدي الإغلاق بواسطة المعلم إلى تعيين sessionEndReason: teacher_stopped وتصبح الحالة abandoned. يؤدي الإغلاق بخطأ داخلي إلى تعيين sessionEndReason: error وتصبح الحالة abandoned. لا توجد في الواجهة البرمجية الحالات started وin_progress وfinished وtime_capped.

سجل الحالات مضاف إليه فقط. يمكن تعديل رأس الجلسة نفسه (لأنه يجب تحديثه مع ورود الإجابات)، لكن كل انتقال بين الحالات يُنشئ صفاً جديداً في status_transition (الكيان student_session). مسار التدقيق لا يُحذف ولا يُعدَّل أبداً.

بدء جلسة

curl -X POST https://localhost:3000/api/diagnostic-sessions/start \ -H "Authorization: Bearer <accessToken>" \ -H "Content-Type: application/json" \ -d '{ "subSkillId": "PHON-01", "assessmentWindowId": "BOY" }'

يتطلب صلاحية USE_DIAGNOSTIC_ENGINE. يضع الخادم قفلاً استشارياً per (org, studentProfileId) بحيث تُعالَج الطلبات المتزامنة لنفس الطالب تسلسلياً بدلاً من التسابق إلى قيد قاعدة البيانات.

إرسال الإجابات

يقبل POST /api/diagnostic-sessions/:id/responses فقط selectedOption؛ لا يُرسل العميل أبداً isCorrect ولا درجة سابقة. يقرأ الخادم الإجابة الصحيحة من بنك الأسئلة ويصنّف الإجابة داخلياً. هذا ضمان أساسي للنزاهة: لا يمكن لحمولة استجابة مزوّرة أن ترفع درجة أو توجّه قيمة θ.

يُسجّل الخادم مهارة فرعية واحدة في كل مرة. في الجلسات متعددة المهارات الفرعية، يعيد العميل استدعاء /responses مع subSkillId التالي بعد إغلاق المهارة الفرعية السابقة.

حد الوقت

يُتابَع الوقت الفعّال (الإجمالي مطروحاً منه فترات الإيقاف المؤقت وإعادة التشغيل الصوتي) في cap.ts. عند الوصول إلى 900,000 ملّي ثانية (15 دقيقة) أو تجاوزها، يُنهي استدعاء /responses التالي الجلسة تلقائياً مع sessionEndReason: time_cap. لا يُعاقَب الطالب على الإيقاف المؤقت أو إعادة الاستماع؛ الوقت الفعّال وحده هو ما يُحسب.

الصوت أولاً للصف الأول

بالنسبة لطلاب الصف الأول، يقرأ الخادم student.gradeLevel لاختيار الأسئلة الصوتية. لا يقرأ أبداً class.grade داخل وحدة الجلسة، يُفرض ذلك بفحص CI يُسمى lint:session-grade-filter. إذا لم يتوفر سؤال صوتي للمهارة الفرعية، يكون firstItemId قيمته null؛ ومنذ 2026-09-07 تُترك هذه الجلسة على الخادم: يحمل الردّ أيضًا noEligibleItem: true وstate.status: "abandoned"، ويحتفظ الصف بـsessionEndReason: "no_eligible_item"، ويُرجع POST /api/diagnostic-sessions/{id}/finish عليها 404. اعرض حالة «لا توجد أسئلة جاهزة الآن» ودع الطالب يبدأ لاحقًا؛ ولا تعاملها أبدًا كجلسة مكتملة. ولا تعود الجلسة إلى سؤال نصي.

المرونة في التخزين المحلي للمتصفح

تعرض كل جلسة طلابية جولة حالة/استئناف (القاعدة الإلزامية B.5). عند استدعاء POST /api/diagnostic-sessions/:id/resume، يُعيد الخادم التحقق من الملكية والمستأجر ونافذة 24 ساعة من lastTouchedAt في قاعدة البيانات، لا من الرمز وحده. رمز مسروق لا فائدة منه عبر الجلسات.

يُرجع GET /api/diagnostic-sessions/:id/state رمزاً موقّعاً (HMAC) يجب أن يخزّنه الطرف الأمامي في localStorage أو sessionStorage. عند التحديث أو تبديل التبويب أو قفل الجهاز، يُقدّم العميل الرمز إلى /resume لاستعادة الجلسة من نقطتها الدقيقة.

يُرجع finish قيمة θ والخطأ المعياري ضمن رمز منسوب للطالب. هذا هو السطح الوحيد الذي تظهر فيه θ في استجابة مصادق عليها بحساب الطالب، وهي مدخل لقرار المعلم. كل استجابة طلابية أخرى يُجرّد منها θ والخطأ المعياري وكفاية البيانات وحالة المهارة ومعلومات الملف والحزمة والطبقة التدريجية بواسطة المسلسِل القائم على القائمة المسموح بها.

المسارات الستة للتشخيص

الطريقةالمسارالصلاحية
POST/api/diagnostic-sessions/startUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
GET/api/diagnostic-sessions/:id/stateUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
POST/api/diagnostic-sessions/:id/resumeUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
POST/api/diagnostic-sessions/:id/responsesUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
POST/api/diagnostic-sessions/:id/finishUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
GET/api/diagnostic-sessions/:idUSE_DIAGNOSTIC_ENGINE + طالب الجلسة أو قراءة معلم الفصل

المحرك (حساب θ)

وحدة المحرك (modules/engine) هي حلقة القياس. تقبل دفعة من الإجابات المصنّفة، وتطبق صيغة الحساب المثبّتة بالمعايرة، وتكتب صفاً غير قابل للتعديل في diagnostic_session، وترجع θ الجديدة والخطأ المعياري والسؤال التالي.

لا نماذج لغوية في حلقة القياس

لا تدخل أي نموذج لغوي في حساب θ أو اختيار الأسئلة أو تصنيف الإجابات. الصيغة حتمية ومثبّتة: المدخلات ذاتها تنتج دائماً θ متطابقة بتةً إلى 4 خانات عشرية (ضمان الإعادة V-6). تُخزَّن θ كـ Decimal(7,4) بدلاً من float للقضاء على الاختلاف بين الأجهزة.

اختيار δ بمسارين

لكل سؤال قيمة deltaPrior (محسوبة عند الاستيراد) وقيمة deltaCalibrated مستقبلية (تكتبها WP-CAL). يستخدم المحرك δ المعيّرة حين تبلغ عتبة الثقة 300 استجابة مقارنة؛ دون ذلك يعود إلى القيمة المبدئية. تُجمَّد نسخة من قيم δ المستخدمة على صف التدقيق حتى تقرأ الإعادة ما استخدمه المحرك فعلاً، لا القيمة الحية في item_bank التي قد يُعيد تشغيل المعايرة كتابتها.

اختيار الأسئلة (CAT)

يختار المحرك السؤال التالي بأقصى معلومات فيشر عند θ الحالية. تُفضّ التعادلات أبجدياً وفق itemId لضمان الحتمية. الأسئلة المقدَّمة مسبقاً خلال الجلسة مستبعدة. لا توجد نافذة صعوبة في مسار التشخيص؛ جميع الأسئلة المؤهلة للمهارة الفرعية تتنافس بمعلومات فيشر وحدها.

المسارات الأربعة للمحرك

الطريقةالمسارالصلاحية
POST/api/engine/scoreUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
GET/api/engine/sessions/{id}USE_DIAGNOSTIC_ENGINE + نطاق المنظمة
GET/api/engine/sessions/by-student/{studentId}USE_DIAGNOSTIC_ENGINE + طالب الجلسة
POST/api/engine/replay/{sessionId}RUN_BACKOFFICE

replay يُعيد تنفيذ مدخلات الجلسة المثبّتة دون كتابة أي شيء. هو مسار التدقيق V-6.

الناتج يُغذّي محرك حالة المهارة

عند اكتمال التشخيص، يكتب الخادم شيئين: صفوف θ per-sub-skill (student_assessment_result) وصف أدلة screening_score مبني على العدّ مُشتَق من الإجابات التي صنّفها الخادم. صف screening_score هو ما تكتبه جسر القياس في جدول الأدلة الخاص بمحرك حالة المهارة وما يستهلكه إعادة حساب المحرك في تشغيله التالي. يقرأ المحرك تلك الأدلة إلى جانب اختبارات CBM وأنواع أدلة أخرى لينتج SkillStatus وDomainStatus لكل مهارة فرعية ونطاق، لتقود تلك الحالات بدورها حلّ الملف وصولاً إلى توصية حزمة التدخل.

التدريب التكيّفي (WP-05)

يُبذر التدريب من تشخيص مكتمل ويدير حلقة تكيّفية per-sub-skill.

بذر قائمة التدريب

POST /api/practice/queues/seed/:diagnosticSessionId يبني قائمة تدريب (student_session نوعها practice) مرتبة من صفوف θ للتشخيص المكتمل. تشمل المهارات الفرعية الضعيفة أو قيد المراقبة فقط (المهارات التي لا تحمل θ، أي البيانات غير الكافية، مستبعدة وفق GSR-01). يُهيّئ البذر أيضاً student_practice_band دائماً per-sub-skill مبتدئاً من θ التشخيص؛ إذا كانت الطبقة موجودة مسبقاً لتلك المهارة الفرعية تُترك دون تغيير (قاعدة الاستمرارية).

في الموجة الأولى، تحمل كل قائمة مهارة فرعية واحدة بالضبط (الأضعف بحسب θ). حين يكون لدى الطالب عدة مهارات فرعية ضعيفة، يُعيد بذر قائمة جديدة لكل مهارة بعد انتهاء السابقة.

اختيار الأسئلة التكيّفي في التدريب

GET /api/practice/queues/current/next-item يُطبّق نافذة صعوبة θ ± 0.5 لوجيت لتضييق المرشّحين قبل اختيار أقصى معلومات فيشر. هذه النافذة قيد خاص بالتدريب فقط؛ لا يملك محرك التشخيص نافذة من هذا النوع. الأسئلة المقدَّمة مسبقاً في القائمة الحالية مستبعدة. تُشتقّ القائمة النشطة من ملف المستخدم المصادق عليه؛ لا يوجد معامل :studentId في المسار، مما يمنع IDOR.

إرسال الإجابات وتوجيه الطبقة

POST /api/practice/queues/current/submit-response يصنّف الإجابة ويحدّث θ ويضيف صفاً في practice_response. حين يكتمل بلوك من 8 أسئلة على الأقل، تُطبَّق قاعدة التوجيه 80/60 على الطبقة الدائمة:

دقة البلوكالإجراء
80% أو أكثررفع الطبقة
60-79%الإبقاء على الطبقة
أقل من 60%خفض الطبقة
أقل من 60% عند طبقة very_easyالتوجيه إلى مهارة فرعية متطلَّبة (لا توجد درجة أدنى)

حين تنخفض دقة البلوك إلى ما دون 60% وكان الطالب في طبقة very_easy بالفعل، يوجّه النظام إلى مهارة فرعية متطلَّبة بدلاً من الخفض أكثر، لأن سلّم الطبقات لا يحمل درجة أدنى من very_easy.

تُكتب تغييرات الطبقة في student_practice_band_history. تستمر الطبقة عبر قوائم البذر المعاد إنشاؤها؛ طالب يعود بعد أسبوع يبدأ من حيث توقف.

استعادة الحالة في التدريب

GET /api/practice/queues/current/state يُرجع رمزاً موقّعاً (HMAC) كما هو الحال في مسار حالة التشخيص. يخزّنه العميل ويقدّمه عند فتح الجلسة التالية لاستعادة الموضع في القائمة، بنفس المرونة المطلوبة عند التحديث وتبديل التطبيق والقفل وانقطاع الاتصال والإغلاق القسري.

المسارات الأربعة للتدريب

الطريقةالمسارالصلاحية
POST/api/practice/queues/seed/:diagnosticSessionIdUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
GET/api/practice/queues/current/next-itemUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
POST/api/practice/queues/current/submit-responseUSE_DIAGNOSTIC_ENGINE + طالب الجلسة
GET/api/practice/queues/current/stateUSE_DIAGNOSTIC_ENGINE + طالب الجلسة

ما لا يصل إلى الطالب

يُجرّد المسلسِل القائم على القائمة المسموح بها (toStudentSessionDTO) كل استجابة طلابية من:

  • θ والخطأ المعياري
  • dataSufficiency
  • SkillStatus وDomainStatus
  • معرّفات الملف الأساسي والمُعدِّل والعلامات الفرعية
  • معلومات الحزمة والطبقة التدريجية
  • تفاصيل نطاق الصعوبة الداخلية

استجابة finish والملخص الموجَّه للمعلم هما السطحان الوحيدان المجاوران لـ θ، والملخص يحذف θ الخام عمداً.

إعادة التشخيص

إعادة التشخيص هي جلسة جديدة في نافذة تقييم لاحقة، لا تفريعة عن الآلية الأصلية. تسلك المسار ذاته للمُنسِّق، لكن مع حارسَين إضافيَّين عند البداية.

بوابة الـ14 يوماً. يتحقق POST /api/diagnostic-sessions/start/re-diagnostic من مرور 14 يوماً على الأقل منذ آخر تشخيص مكتمل لنفس الطالب والدورة ذاتها (يُقاس من finishedAt لا من startedAt). يُعيد الطلب داخل البوابة 409 مع reason: rediag_too_soon وطابع زمني earliestAllowed. إذا لم يكن هناك تشخيص سابق مكتمل، تُعفى البوابة وتُفتح الجلسة بصورة عادية.

أرشفة قائمة التدريب السابقة. قبل فتح الجلسة الجديدة، تُؤرشَف جميع قوائم التدريب المفتوحة للطالب (يُضبط archivedAt، وتتغير الحالة إلى abandoned). تمر الأرشفة عبر مسار الكتابة الأحادي mutateQueue. إذا نشطت قبلها ثابتة الجلسة الواحدة (جلسة مفتوحة موجودة فعلاً للدورة)، تُتخطى الأرشفة كليًا لمنع فقدان بيانات خطة التدريب بصمت.

المقارنة بين الجلسة السابقة والجديدة شأن قرائي تتولاه وحدة التقارير.

الأنظمة المرتبطة

نوافذ التقييم (2026-09-09)

يُعرض التقييم على الطفل فقط داخل نافذة تقييم مفتوحة. يخبر GET /api/student/today-plan أيّها (screening: { window, source } — يكون source هو class_trigger عندما تفتحه المعلّمة لصفّها، وإلّا النطاق الذي ينطبق تقويمه) ويُرجع null عندما لا شيء مفتوح أو عندما أكمل الطفل تلك النافذة هذه السنة الدراسية. لم يعد POST /api/diagnostic-sessions/start يحتاج screeningCycle: أغفله فيحدّد الخادم النافذة بالطريقة نفسها؛ وينال الطالب الذي يسمّي نافذة غير مفتوحة 409 window_mismatch، وعندما لا شيء مفتوح 409 no_open_window (لا تُنشأ جلسة — اعرض حالة هادئة «لا يوجد تقييم مفتوح الآن»). تسجّل الجلسة screeningSource. يضبط المشرفون التواريخ عبر /api/admin/screening-windows؛ وتفتح المعلّمات تقييم الصف عبر /api/classes/{classId}/screening-triggers.