CBT Exam Workspace & Enrollment — Implementation Plan¶
Tanggal: 2026-06-11
Versi: 1.2
Status: Active — Phase 1–3 selesai
Mode: Server · Root: /home/scola/odoo
Dokumen acuan¶
| Dokumen | Peran |
|---|---|
docs/ai-guidelines/development-guide.md |
Workflow QC & commit |
docs/ai-guidelines/architecture-api.md |
Same-origin API |
docs/domains/cbt/README.md |
SSOT domain CBT |
docs/domains/cbt/fe-be-question-exam-sync-recon-2026-05-27.md |
Kontrak FE/BE §5.5–5.6 |
docs/domains/cbt/cbt-qa-runbook-2026-05-28.md |
Diagnosa enroll independent |
docs/qa/cbt-independent-admission-uat-end-to-end.md |
UAT admission/independent |
docs/user-guide/quick-start/buat-ujian-cbt.md |
Quick start operator |
1. Latar belakang & masalah¶
1.1 Gejala operasional (sebelum perbaikan)¶
- Guru membuat ujian Independent, memilih rombel, mengira siswa otomatis terdaftar — padahal enroll batch hanya jalan saat create dan satu rombel.
- Halaman
/faculty/cbt/exams/:idterasa read-only + modal terfragmentasi; penambahan peserta internal satu per satu. - Import Peserta hanya bulk untuk peserta external (
Nama,KODE), bukan siswa sekolah. - Dropdown rombel menampilkan
[CBT] Independent/[CBT] Admission— rombel teknis sistem, bukan kelompok peserta. - Admission seharusnya sync dari SPMB; UI detail masih menampilkan pola manual seperti independent.
1.2 Root cause (audit teknis — historis)¶
| Lapisan | Temuan awal | Status setelah Phase 1–3 |
|---|---|---|
| BE | _seed_independent_participants_from_batch hanya di create_exam |
✅ API enroll-batches post-create |
| BE | lookup/batches mengembalikan rombel [CBT]* |
✅ Filter purpose=enrollment |
| BE | Import tidak resolve NIS/NISN | ✅ Resolve nid/nsn/student_id |
| FE | Monolith detail + Edit modal terfragmentasi | ✅ Tab workspace + Pengaturan inline |
| FE | Create form duplikat detail | ✅ Create ringkas → redirect tab Pengaturan |
1.3 Target UX (best practice — tercapai)¶
Satu Exam Management Workspace per ujian dengan:
- Tab Ringkasan — status, token, checklist siap publish
- Tab Pengaturan — semua field editable (dengan lock per state)
- Tab Peserta — Enrollment Center (multi-rombel, import internal/external, bulk block/remove)
- Tab Penilaian — reuse section existing
Context-aware:
- Independent → Enrollment Center penuh
- Admission → panel Integrasi SPMB (read-only peserta + link sync)
- Learning → attendee dari rombel (existing)
1.4 Route workspace (Phase 3)¶
| Route | Tab default | Catatan |
|---|---|---|
/faculty/cbt/exams/:examId |
overview |
Faculty independent/admission |
/faculty/cbt/exams/:examId/settings |
settings |
Redirect setelah create ringkas |
/faculty/cbt/exams/:examId/participants |
participants |
Deep-link enrollment |
/faculty/cbt/exams/:examId/grading |
grading |
Deep-link penilaian |
/admin/spmb/cbt/exams/:examId/:tab? |
sama | SPMB admin |
/admin-staff/admissions/cbt/exams/:examId/:tab? |
sama | Admin staff SPMB |
Konstanta tab: src/constants/cbtExamWorkspace.js (overview, settings, participants, grading).
2. Arsitektur target¶
flowchart LR
subgraph fe ["Frontend"]
List["CBTExamList<br/>create ringkas"]
Workspace["CBTExamDetail<br/>Kelola Ujian + Tabs"]
Enroll["CBTExamEnrollmentPanel"]
Checklist["CBTExamPublishChecklist"]
Admission["CBTExamAdmissionSyncPanel"]
end
subgraph be ["Backend scola_cbt"]
Lookup["lookup/batches?purpose=enrollment"]
EnrollAPI["participants/enroll-batches"]
BulkAPI["participants/bulk-state|bulk-remove"]
ImportAPI["participants/import"]
Seed["_seed_independent_participants_from_batch"]
end
List -->|"redirect settings"| Workspace
Workspace --> Enroll
Workspace --> Checklist
Workspace --> Admission
Enroll --> Lookup
Enroll --> EnrollAPI
Enroll --> BulkAPI
Enroll --> ImportAPI
EnrollAPI --> Seed
3. Fase implementasi¶
Phase 1 — P0 Quick wins ✅¶
| ID | Item | BE | FE | Acceptance |
|---|---|---|---|---|
| P1-01 | Filter rombel teknis [CBT]* dari dropdown enrollment |
✅ | ✅ | Dropdown enroll tidak menampilkan [CBT] Independent/Admission |
| P1-02 | min_marks + remedial di form edit |
— | ✅ | KKM dapat diedit saat draft/schedule (tab Pengaturan) |
| P1-03 | Rename UX: Detail → Kelola Ujian; Participant → Peserta | — | ✅ | Label konsisten Bahasa Indonesia |
| P1-04 | Import internal: NIS/NISN per baris | ✅ | ✅ | Paste NIS/NISN → participant student |
| P1-05 | Checklist siap publish + empty state actionable | — | ✅ | Banner peringatan jika independent tanpa peserta |
| P1-06 | Admission: panel Integrasi SPMB | — | ✅ | Tombol manual import disembunyikan; hint sync jadwal |
Phase 2 — P1 Enrollment Center ✅¶
| ID | Item | BE | FE | Acceptance |
|---|---|---|---|---|
| P2-01 | API POST .../participants/enroll-batches |
✅ | ✅ | Multi-rombel post-create; dry_run preview |
| P2-02 | student_count di lookup batches enrollment |
✅ | ✅ | Preview jumlah siswa per rombel |
| P2-03 | Tab workspace di detail ujian | — | ✅ | AppTabs: Ringkasan, Pengaturan, Peserta, Penilaian |
| P2-04 | Komponen CBTExamEnrollmentPanel |
— | ✅ | Multi-select rombel + enroll + import tabs |
| P2-05 | Lookup siswa by batch_id |
✅ | ✅ | Filter siswa saat tambah manual per rombel |
Phase 3 — P2 Polish ✅¶
| ID | Item | Catatan | Status |
|---|---|---|---|
| P3-01 | Create ringkas → redirect workspace Tab Pengaturan | Modal create: nama + context + tipe + question set | ✅ |
| P3-02 | Bulk block/remove peserta (checkbox multi-select) | API bulk-state / bulk-remove + panel |
✅ |
| P3-03 | E2E multi-rombel enroll → student schedule | SM-CBT-008 |
✅ |
| P3-04 | Sub-route /exams/:id/:tab? |
Sync tab ↔ URL via cbtExamWorkspace.js |
✅ |
| P3-05 | CSV upload template download | Unduh template + upload file di import modal | ✅ |
4. Kontrak API baru / diperluas¶
4.1 POST /api/scola_cbt/lookup/batches¶
Params tambahan:
{
"active_only": true,
"limit": 100,
"purpose": "enrollment"
}
Perilaku purpose=enrollment:
- Exclude batch dengan
namediawali[CBT](rombel teknis non-learning). - Response tambahan:
student_countper batch.
4.2 POST /api/scola_cbt/exams/<exam_id>/participants/enroll-batches¶
Request:
{
"batch_ids": [12, 15, 18],
"skip_existing": true,
"dry_run": false
}
Response:
{
"success": true,
"data": {
"batch_count": 3,
"created": 85,
"skipped": 2,
"failed": 0,
"details": [
{ "batch_id": 12, "batch_name": "8A", "created": 32, "skipped": 0 }
]
}
}
Guard:
- Hanya
exam_context === 'independent'. - State exam:
draftatauschedule(tidak saatheld/done). - User: author/proctor manager/CBT manager (sama dengan create participant).
4.3 POST /api/scola_cbt/exams/<exam_id>/participants/import¶
Row extended (internal):
{ "identity_type": "student", "nid": "3275081501120001" }
{ "identity_type": "student", "nsn": "0057348291" }
{ "identity_type": "student", "student_id": 42 }
Backend resolve nid/nsn → student_id sebelum get_or_create_for_student.
4.4 POST /api/scola_cbt/lookup/students¶
Params tambahan: batch_id (optional) — filter siswa by rombel.
4.5 POST /api/scola_cbt/exams/<exam_id>/participants/bulk-state¶
Request:
{
"participant_ids": [1, 2, 3],
"state": "blocked"
}
Guard: sama dengan enroll-batches (independent, draft/schedule).
4.6 POST /api/scola_cbt/exams/<exam_id>/participants/bulk-remove¶
Request:
{
"participant_ids": [1, 2, 3]
}
Response: summary removed, deactivated, failed (attempt aktif dinonaktifkan, bukan hard-delete).
5. Perubahan frontend (file map)¶
| File | Perubahan |
|---|---|
src/constants/cbtExamWorkspace.js |
Baru — tab valid + helper route params |
src/services/cbt/examService.js |
listEnrollmentBatches, enrollParticipantsFromBatches, listCbtStudents, bulkUpdateParticipantState, bulkRemoveParticipants |
src/components/CBT/CBTExamEnrollmentPanel.vue |
Enrollment center: multi-rombel, import, bulk, CSV template |
src/components/CBT/CBTExamPublishChecklist.vue |
Readiness checklist publish |
src/components/CBT/CBTExamAdmissionSyncPanel.vue |
Hint SPMB sync |
src/views/ExamManagement/Faculty/Exam/CBTExamDetail.vue |
Tabs workspace, tab Pengaturan inline, route sync |
src/views/ExamManagement/Faculty/Exam/CBTExamList.vue |
Create ringkas, redirect settings |
src/router/teacherRouteFragments/assessment.js |
Route :examId/:tab(overview\|settings\|participants\|grading)? |
src/router/admissionsRoutes.js |
Route tab sama untuk SPMB/admin-staff |
tests/e2e/smoke/cbt_independent_enrollment.spec.ts |
SM-CBT-008 multi-rombel enroll |
tests/e2e/helpers/cbt.ts |
fetchEnrollmentBatchIds, enrollExamBatches |
6. Perubahan backend (file map)¶
| File | Perubahan |
|---|---|
scola_cbt/controllers/cbt_api.py |
Shared _seed_independent_participants_from_batch |
scola_cbt/controllers/exam.py |
Delegate ke shared seed |
scola_cbt/controllers/participant.py |
enroll_batches, bulk-state, bulk-remove, resolve nid/nsn import |
scola_cbt/controllers/exam_lookup_api.py |
Filter [CBT]*, student_count, batch_id filter students |
scola_cbt/tests/test_participant_enrollment_unit.py |
Unit test enroll-batches seed |
scola_cbt/__manifest__.py |
17.0.1.0.12 (Phase 2), 17.0.1.0.13 (bulk Phase 3) |
7. Aturan edit per state exam¶
| Field | draft | schedule | held | done |
|---|---|---|---|---|
| Nama, question set, jadwal, durasi, KKM | ✅ | ✅ | ❌ | ❌ |
| Shuffle, show result, remedial | ✅ | ✅ | ❌ | ❌ |
| Enroll rombel / import peserta | ✅ | ✅ | ❌ | ❌ |
| Bulk block/remove peserta | ✅ | ✅ | ❌ | ❌ |
| Token regenerate | ✅ | ✅ | ⚠️ policy | ❌ |
| Publish / transition | ✅ | ✅ | — | — |
8. Alur operator (Independent — setelah Phase 3)¶
- Buat ujian di
/faculty/cbt/exams→ modal ringkas (nama, context, tipe, question set). - Redirect otomatis ke tab Pengaturan → lengkapi jadwal, durasi, KKM, opsi CBT → Simpan Pengaturan.
- Tab Peserta → pilih satu atau lebih rombel → Daftarkan dari rombel (preview opsional).
- Alternatif: import NIS/NISN (internal) atau
Nama,KODE(external) via modal import + template CSV. - Tab Ringkasan → cek checklist publish → transisi draft → schedule → held.
- Siswa internal:
/student/cbt— peserta external:/cbt/public.
9. QC gates¶
Backend¶
python3 -m py_compile custom_addons_scola/gcgscola/scola_cbt/controllers/*.py
python3 custom_addons_scola/gcgscola/scola_cbt/tests/test_participant_enrollment_unit.py
# + existing scola_cbt unit suites
Frontend¶
cd scola-fe-v2
npm run lint:diff
npm run type-check
npm run build
npx playwright test tests/e2e/smoke/cbt_independent_enrollment.spec.ts --workers=1
Manual smoke (Independent)¶
- Buat ujian independent → redirect tab Pengaturan → simpan jadwal.
- Tab Peserta → multi-rombel → Daftarkan dari rombel.
- Login siswa →
/student/cbt→ ujian muncul. - Import NIS/NISN internal + external
Nama,KODE; unduh/upload template CSV. - Bulk block/remove via checkbox tabel peserta.
- Negative: rombel
[CBT]*tidak muncul di dropdown enrollment.
Manual smoke (Admission)¶
- Detail ujian admission → panel SPMB, tanpa tombol import manual.
- Sync peserta dari
/admin/spmb/testtab CBT Admission.
10. Risiko & mitigasi¶
| Risiko | Mitigasi |
|---|---|
| Enroll massal duplikat | get_or_create_for_student + skip_existing |
| Enroll saat ujian sudah held | API reject state held/done |
| Performance multi-rombel besar | Batch loop per rombel; audit log metadata |
| Regression learning/admission | Context guard di setiap endpoint baru |
| Bulk remove saat attempt aktif | Deactivate participant, bukan hard-delete |
11. Definisi selesai (Phase 1–3)¶
- [x] Dokumen plan (file ini)
- [x] API enroll-batches + lookup filter (BE
17.0.1.0.12) - [x] FE workspace tabs + enrollment panel
- [x] Unit test enroll seed (
test_participant_enrollment_unit.py) - [x] Phase 3: create ringkas, tab Pengaturan, route tab, bulk peserta, CSV import, E2E
SM-CBT-008 - [ ] Manual smoke P1-01–P3-05 PASS (operator)
- [x] UAT doc diperbarui (
cbt-manual-uat.md§9, quick-start, domain README)
12. Riwayat¶
| Tanggal | Versi | Perubahan |
|---|---|---|
| 2026-06-13 | 1.0 | Dokumen awal + kickoff implementasi Phase 1–2 |
| 2026-06-11 | 1.1 | Phase 3 polish: tab Pengaturan, create ringkas, bulk peserta, route tab, E2E SM-CBT-008 |
| 2026-06-11 | 1.2 | Dokumentasi diselaraskan: API bulk, alur operator, definisi selesai Phase 1–3 |