FE-BE CBT Question, Exam, and Bank Soal Sync Recon¶
- Tanggal recon: 2026-05-27, Asia/Jakarta
- Mode kerja: local repo recon, docs-only
- Frontend root:
/Users/salfath/scola-fe-v2 - Backend root:
/Users/salfath/scola-odoo-module
Dokumen ini merangkum hasil recon mendalam atas konsistensi implementasi frontend dan backend untuk area:
- Bank soal / question bank
- Pembuatan dan pengelolaan ujian CBT
- Runner pengerjaan ujian
- Peserta, proctoring, grading, hasil, analytics
- Context
learning,independent, danadmission - Jembatan SPMB CBT
- Feature flag, capability, dan access control
1. Dokumen Tata Kelola Yang Dibaca¶
Sesuai instruksi AGENTS.md, recon dimulai dengan membaca dokumen aktif berikut:
docs/ai-guidelines/development-guide.mddocs/ai-guidelines/workspace-governance.mddocs/ai-guidelines/architecture-api.mddocs/qa/testing-guidelines.md
Catatan tata kelola:
- Frontend repo adalah SSOT dokumentasi UI/domain untuk perubahan ini.
- Backend repo yang direkon adalah
/Users/salfath/scola-odoo-module. - Tidak ada perubahan kode aplikasi dalam recon ini.
- Dokumentasi baru ditempatkan di path frontend yang diizinkan:
docs/domains/cbt/. - Arsitektur same-origin API tetap konsisten. Service frontend memanggil namespace
/scola_cbt/...melaluiapiClientyang memiliki base/api, sehingga endpoint efektif menjadi/api/scola_cbt/....
2. Ringkasan Eksekutif¶
Implementasi FE-BE untuk CBT sudah cukup sinkron di jalur utama:
- Namespace endpoint FE dan BE konsisten untuk
question_set,question,exam,runner,grading,proctor,results,analytics,export,policy, danaudit. - Model backend
scola.cbt.question.set,scola.cbt.question.item,scola.cbt.question.choice,scola.cbt.question.asset,op.exam,scola.cbt.participant,scola.cbt.attempt, danscola.cbt.attempt.answerselaras dengan payload utama yang dikirim FE. - Workflow
learning,independent, danadmissionsudah dipisahkan di FE dan BE. - Jalur SPMB CBT sudah terhubung: FE memilih
cbt_exam_idcontext admission pada jadwal SPMB, BE memastikan exam benar-benar CBT admission dan membuat/sinkron participant serta nilai. - Access control mayoritas sinkron: FE memakai capability/feature flag, BE memakai Odoo group, assigned staff, ownership, faculty scope, dan admission context manager.
Temuan utama yang perlu ditindaklanjuti bukan pada route dasar, tetapi pada semantik fitur:
- Beberapa field policy muncul dan bisa disimpan dari FE, tetapi belum dipakai penuh di runtime BE:
grace_period_minutes,block_multi_login,allow_negative_marking,negative_mark_percentage, danshow_score_immediately. - Import soal mendukung tipe
mcq_multi, tetapi format CSV FE/BE saat ini hanya menerima satucorrect_answerA-E, sehingga multi-correct import belum benar-benar tersedia. - Public CBT route FE tidak memiliki
featureFlag: "scola_cbt", berbeda dengan route CBT authenticated. - Route detail pendaftar SPMB memasang
featureFlag: "scola_cbt"pada parent detail admission, sehingga bagian non-CBT detail pendaftar berisiko ikut tertutup pada tenant tanpa CBT.
3. Scope File Utama¶
3.1 Frontend¶
Service:
src/api/apiServices.jssrc/services/cbt/questionService.jssrc/services/cbt/examService.jssrc/services/cbt/cbtRunnerService.jssrc/services/cbt/gradingService.jssrc/services/cbt/proctorService.jssrc/services/cbt/examResults.service.jssrc/services/cbt/cbtService.jssrc/services/spmb/spmbAdminTestSchedule.service.js
Views dan composables:
src/views/ExamManagement/Faculty/QuestionBank/CBTQuestionList.vuesrc/views/ExamManagement/Faculty/QuestionBank/CBTQuestionSetDetail.vuesrc/views/ExamManagement/Faculty/QuestionBank/CBTQuestionForm.vuesrc/views/ExamManagement/Faculty/Exam/CBTExamList.vuesrc/views/ExamManagement/Faculty/Exam/CBTExamDetail.vuesrc/views/AssignmentManagement/Faculty/AddExamPage.vuesrc/views/AssignmentManagement/Faculty/components/EditExamModal.vuesrc/views/ExamManagement/Student/CBTRunner.vuesrc/composables/cbt/useExamState.jssrc/views/AdmissionManagement/TestSchedule.vuesrc/views/AdminViews/CBT/CBTSettings.vue
Routes, access, and tests:
src/router/teacherRouteFragments/assessment.jssrc/router/studentRouteFragments/learning.jssrc/router/spmbRoutes.jssrc/router/admissionsRoutes.jssrc/router/sharedExperienceRoutes.jssrc/config/capabilityFragments/learningAssessmentCapabilities.jssrc/config/capabilityFragments/admissionsCapabilities.jssrc/config/roleFragments/operationalRoles.jssrc/config/roleFragments/portalRoles.jssrc/access/featureFlagFragments/learningAssessmentAccess.jstests/menu/cbtMenuRouteRbacSync.spec.jstests/menu/teacherCbtFeatureFlag.spec.jstests/menu/teacherCapabilityCoverage.spec.jstests/menu/routerFeatureFlagGuard.spec.js
3.2 Backend¶
CBT module:
scola_cbt/controllers/question_set_api.pyscola_cbt/controllers/question_api.pyscola_cbt/controllers/exam.pyscola_cbt/controllers/exam_read_api.pyscola_cbt/controllers/exam_lifecycle_api.pyscola_cbt/controllers/exam_attendee_api.pyscola_cbt/controllers/participant.pyscola_cbt/controllers/exam_lookup_api.pyscola_cbt/controllers/exam_staff_api.pyscola_cbt/controllers/runner_start_api.pyscola_cbt/controllers/runner_attempt_api.pyscola_cbt/controllers/runner_integrity_api.pyscola_cbt/controllers/grading_read_api.pyscola_cbt/controllers/grading_write_api.pyscola_cbt/controllers/proctor_read_api.pyscola_cbt/controllers/proctor_action_api.pyscola_cbt/controllers/report_results_api.pyscola_cbt/controllers/report_analytics_api.pyscola_cbt/controllers/report_export_api.pyscola_cbt/controllers/main.py
Backend models/security:
scola_cbt/models/question_set.pyscola_cbt/models/question.pyscola_cbt/models/exam.pyscola_cbt/models/participant.pyscola_cbt/models/attempt.pyscola_cbt/models/policy.pyscola_cbt/models/assessment_grade.pyscola_cbt/models/item_analysis.pyscola_cbt/security/ir.model.access.csvscola_cbt/security/cbt_security.xmlscola_cbt/security/question_set_rules.xmlscola_cbt/security/spmb_cbt_bridge.xml
SPMB bridge:
scola_admission_assessment/models/admission_test.pyscola_admission_assessment/models/cbt_participant.pyscola_admission_assessment/controllers/spmb_assessment_schedules_api.pyscola_admission/controllers/spmb_complaints_ranking_api.py
4. API Architecture Sync¶
4.1 Same-Origin Contract¶
Frontend service memanggil endpoint seperti:
/scola_cbt/question_set/list/scola_cbt/exams/create/scola_cbt/runner/start
Karena apiClient memakai base URL /api, request efektif menjadi:
/api/scola_cbt/question_set/list/api/scola_cbt/exams/create/api/scola_cbt/runner/start
Backend controller mendeklarasikan route dengan prefix /api/scola_cbt/.... Ini konsisten dengan docs/ai-guidelines/architecture-api.md.
Export XLSX adalah pengecualian yang memang memakai URL GET absolut:
- FE:
/api/scola_cbt/export/exam/:id/xlsx - BE:
/api/scola_cbt/export/exam/<id>/xlsx - FE:
/api/scola_cbt/export/integrity/:id/xlsx - BE:
/api/scola_cbt/export/integrity/<id>/xlsx
4.2 JSON-RPC Shape¶
Service FE umumnya mengirim:
{
"jsonrpc": "2.0",
"method": "call",
"params": {}
}
Backend route bertipe type='json' dan membaca argumen langsung dari params. Bentuk ini sinkron.
5. Endpoint Contract Matrix¶
5.1 Bank Soal: Question Set¶
| FE service | BE route | Status | Catatan |
|---|---|---|---|
listQuestionSets(filters, offset, limit) |
/api/scola_cbt/question_set/list |
OK | Filter subject_id, search, offset, limit sinkron. |
getQuestionSet(id) |
/api/scola_cbt/question_set/get |
OK | BE return question_set; FE mengonsumsi detail set. |
createQuestionSet(values) |
/api/scola_cbt/question_set/create |
OK | name, description, subject_id sinkron. |
updateQuestionSet(id, values) |
/api/scola_cbt/question_set/update |
OK | BE batasi manager atau owner set. |
deleteQuestionSet(id) |
/api/scola_cbt/question_set/delete |
OK | BE block jika set dipakai exam active/non-cancel. |
Catatan:
- FE sorting question set dilakukan lokal atas halaman data yang sedang tampil. BE menerima argumen
order, tetapi wrapper FE belum mengeksposnya. Jika sorting di UI dimaksudkan global lintas halaman, kontrak belum lengkap.
5.2 Bank Soal: Question Item, Choice, Asset, Tag¶
| FE service | BE route | Status | Catatan |
|---|---|---|---|
listQuestions(setId) |
/api/scola_cbt/question/list |
OK | FE kirim question_set_id; BE return item, choices, assets, tags. |
getQuestion(id) |
/api/scola_cbt/question/get |
OK | Dipakai form edit. |
createQuestion(questionSetId, values) |
/api/scola_cbt/question/create |
OK | question_set_id, question_type, content, explanation, difficulty, points, tag_ids, choices, assets. |
updateQuestion(id, values) |
/api/scola_cbt/question/update |
OK | BE versioning saat soal yang sudah dipakai exam diedit. |
deleteQuestion(id) |
/api/scola_cbt/question/delete |
Partial | Endpoint ada, tetapi BE membatasi ke CBT Manager; FE utama tidak mengekspos aksi delete item. |
importQuestions(setId, fileContent, fileName) |
/api/scola_cbt/question/import |
Partial | CSV import OK untuk single answer; multi-correct belum lengkap. |
listSubjects() |
/api/scola_cbt/subject/list |
OK | Subject lookup untuk question set/form. |
listTags(subjectId) |
/api/scola_cbt/tag/list |
OK | BE saat ini lebih permisif: auth user dapat list tag. |
createTag(name, colorIndex, subjectId) |
/api/scola_cbt/tag/create |
OK | Dipakai FE untuk tag baru dari form soal. |
Tipe soal yang sinkron:
mcqmcq_multitrue_falseshort_answeressay
Kontrak asset sinkron:
asset_type:image,audio,video,document- Sumber asset:
urlataufile_content+file_name - Existing asset dapat dipertahankan via
iddan metadata
Gap import multi-correct:
- FE form manual mendukung
mcq_multidengan banyak pilihan benar. - Runner dan BE scoring mendukung
mcq_multidengan partial credit. - Tetapi import CSV FE/BE memakai kolom
correct_answertunggal A-E. - Backend import juga memvalidasi
correct_answersebagai satu huruf A-E. - Dampak:
mcq_multihasil import hanya dapat memiliki satu jawaban benar, walaupun tipe soal mendukung multi answer.
5.3 Exam Management¶
| FE service/view | BE route/model | Status | Catatan |
|---|---|---|---|
listExams(filters, offset, limit) |
/api/scola_cbt/exams |
OK | Filter exam_context, exclude_learning, subject_id, state, search sinkron. |
getExam(id) |
/api/scola_cbt/exams/<id> |
OK | Return includes staff flags: can_manage_staff, can_proctor, can_grade. |
createExam(values) |
/api/scola_cbt/exams/create |
OK | question_set_id menentukan is_cbt; learning CBT wajib grade_component_type. |
updateExam(id, values) |
/api/scola_cbt/exams/<id>/update |
OK | Field CBT, timing, marks, remedial, grade mapping sinkron. |
deleteExam(id) |
/api/scola_cbt/exams/<id>/delete |
OK | BE hanya izinkan draft/cancel. |
transitionExamState(id, state) |
/api/scola_cbt/exams/<id>/transition |
OK | FE state action cocok dengan BE transition. |
regenerateToken(id) |
/api/scola_cbt/exams/<id>/regenerate-token |
OK | FE update token dari response. |
listExamTypes() |
/api/scola_cbt/lookup/exam-types |
OK | Lookup exam type. |
listExamBatches() |
/api/scola_cbt/lookup/batches |
OK | Non-learning form menjadikan batch optional; BE auto-create subject/batch/session untuk non-learning bila perlu. |
listCbtStudents() |
/api/scola_cbt/lookup/students |
OK | Dipakai attendee/participant manual. |
listCbtStaffUsers() |
/api/scola_cbt/lookup/staff-users |
OK | BE requires create/manage access. |
updateExamStaff() |
/api/scola_cbt/exams/<id>/staff/update |
OK | BE juga memberi Odoo group ke assigned author/proctor/grader. |
5.4 Learning Exam Flow¶
Frontend learning CBT dibuat dari:
src/views/AssignmentManagement/Faculty/AddExamPage.vuesrc/views/AssignmentManagement/Faculty/components/EditExamModal.vue
Contract:
- FE wajib memilih course/batch/subject.
- FE mengirim
exam_context: "learning". - FE mengirim
question_set_iduntuk mengaktifkan CBT. - FE mewajibkan
grade_component_typeketikaquestion_set_iddipilih. - BE juga menolak learning CBT tanpa
grade_component_type. - BE membuat attendees dari batch dan menyinkron nilai ke
op.exam.attendees. - BE dapat publish ke
academic.gradeuntuk learning CBT yang punya mapping komponen nilai.
Status: sinkron.
5.5 Independent and Admission Exam Flow¶
Frontend non-learning/admission CBT dibuat dari:
src/views/ExamManagement/Faculty/Exam/CBTExamList.vue- Reused oleh admin/admin_staff SPMB routes via route meta
examContext: "admission"
Contract:
- Default non-forced context adalah
independent. - Route SPMB internal memaksa
exam_context: "admission". - FE mewajibkan
question_set_id. - BE membuat exam non-learning dengan helper subject/batch/session jika field learning tidak tersedia.
- Participant manual/import hanya benar-benar berlaku untuk
independent. - Admission participant dikelola dari SPMB schedule bridge, bukan dari manual participant import.
Status: sinkron.
5.6 Attendees and Participants¶
| Context | FE behavior | BE behavior | Status |
|---|---|---|---|
learning |
List/add/remove attendee siswa internal. | op.exam.attendees; transition schedule/held memastikan peserta learning. |
OK |
independent |
Manual participant create/import/state/remove. | scola.cbt.participant dengan identity student atau external; public runner butuh participant code. |
OK |
admission |
Participant berasal dari jadwal SPMB; manual remove/import dicegah UI dan BE. | scola_admission_assessment membuat participant identity admission dari slot pendaftar. |
OK |
5.7 Runner¶
| FE service | BE route | Status | Catatan |
|---|---|---|---|
startAttempt(examId, token, { participantCode }) |
/api/scola_cbt/runner/start |
OK | Public only independent; authenticated admission dapat start tanpa token. |
getAttemptState(attemptId) |
/api/scola_cbt/runner/state |
OK | FE polling untuk force-submit/proctor state. |
saveAnswer(attemptId, questionId, answerData) |
/api/scola_cbt/runner/answer |
OK | Single answer autosave. |
syncAnswers(attemptId, answers) |
/api/scola_cbt/runner/sync |
OK | Bulk sync dirty answers. |
submitAttempt(attemptId) |
/api/scola_cbt/runner/submit |
OK | BE auto-score objective questions. |
logIntegrityEvents(attemptId, events) |
/api/scola_cbt/runner/integrity |
OK | FE event names match backend accepted values. |
Answer payload sync:
- Single choice / true-false:
answer_option_ids: [choiceId] - Multi choice:
answer_option_ids: [choiceId, ...] - Essay / short answer:
answer_char: string
Timer contract:
- BE calculates attempt deadline from
cbt_duration, capped byexam.end_time. - FE countdown is hydrated from
remaining_seconds. - FE periodically refreshes state from BE.
- BE auto-submits on timeout during save/sync.
5.8 Grading¶
| FE service | BE route | Status | Catatan |
|---|---|---|---|
getAttemptForGrading(attemptId) |
/api/scola_cbt/grading/attempt/<id> |
OK | Includes question, answers, assets, choices, rubric/revision data. |
scoreAnswer(payload) |
/api/scola_cbt/grading/score |
OK | attempt_id, answer_id, score, comment, reason. |
finalizeGrading(attemptId) |
/api/scola_cbt/grading/finalize/<id> |
OK | Attempt moves to graded; score sync downstream. |
BE grading access permits:
- CBT manager
- CBT grader
- assigned grader
- admission context manager
- faculty managing the exam
FE teacher grading route uses academics.cbt_exams.manage, not academics.cbt_grading.manage. This matches existing test expectation that teacher-managed exams remain reachable, but leaves academics.cbt_grading.manage underused for academic teacher routes.
5.9 Proctor¶
| FE service | BE route | Status | Catatan |
|---|---|---|---|
listProctorExams() |
/api/scola_cbt/proctor/exams |
OK | Access via assigned proctor/manager/admission manager. |
getProctorExamStatus(id) |
/api/scola_cbt/proctor/exam/<id>/status |
OK | Monitoring attempt status. |
getAttemptFlags(id) |
/api/scola_cbt/proctor/attempt/<id>/flags |
OK | Integrity flags. |
extendTime(id, minutes, reason) |
/api/scola_cbt/proctor/attempt/<id>/extend_time |
OK | Extends attempt deadline. |
forceSubmit(id, reason) |
/api/scola_cbt/proctor/attempt/<id>/force_submit |
OK | FE runner detects submitted state. |
resetAttempt(id, reason) |
/api/scola_cbt/proctor/attempt/<id>/reset |
OK | Reset attempt for retake. |
Important sync point:
- FE generic teacher role intentionally cannot open
/faculty/cbt/proctor. - BE also does not allow generic teacher to proctor unless assigned proctor or privileged group.
- This is enforced by FE test
teacherCapabilityCoverage.spec.js.
5.10 Results, Analytics, Export¶
| FE service | BE route | Status | Catatan |
|---|---|---|---|
getExamResults(examId) |
/api/scola_cbt/results/exam/<id> |
OK | FE normalizes rows/statistics/pipeline. |
publishExamResults(examId) |
/api/scola_cbt/results/exam/<id>/publish |
OK | Learning gradebook path only. |
getExamAnalytics(examId) |
/api/scola_cbt/analytics/exam/<id> |
OK | Score distribution. |
getIntegrityAnalytics(examId) |
/api/scola_cbt/analytics/integrity/<id> |
OK | Integrity report. |
getOperationalAnalytics(examId) |
/api/scola_cbt/analytics/operations/<id> |
OK | Participant monitoring. |
| export exam XLSX | /api/scola_cbt/export/exam/<id>/xlsx |
OK | GET URL used directly. |
| export integrity XLSX | /api/scola_cbt/export/integrity/<id>/xlsx |
OK | GET URL used directly. |
6. Data Model Sync¶
6.1 Question Set¶
BE model scola.cbt.question.set:
codenamedescriptionsubject_idquestion_ids- computed
question_count - computed
total_points created_bycompany_id
FE question set views consume the same fields for list/detail/cards.
Status: OK.
6.2 Question Item¶
BE model scola.cbt.question.item:
question_set_idsubject_idrelated from setquestion_typecontentexplanationdifficultypointstag_idschoice_idsasset_idsrubric_data- versioning fields
FE question form sends and reads matching fields. Versioning is backend-owned and does not require FE-specific payload.
Status: OK.
6.3 Choice¶
BE model scola.cbt.question.choice:
question_idcontentis_correctpoints_if_selected- constraint:
mcqmax one correct answer
FE form enforces:
- objective question requires at least 2 options
- objective question requires at least 1 correct option
mcqUI effectively serializes one correct answermcq_multiserializes multiple correct answers
Status: OK for manual authoring; partial for CSV import.
6.4 Asset¶
BE model scola.cbt.question.asset:
asset_typenameurlfilefile_namemime_type- question relation
FE supports:
- image, audio, video, document
- URL source
- uploaded file encoded as base64
- preserving existing uploaded file on edit
Status: OK.
6.5 Exam¶
BE extends op.exam with CBT fields:
is_cbtexam_contextquestion_set_idcbt_durationcbt_tokencbt_shuffle_questionscbt_shuffle_optionscbt_show_resultcbt_author_user_idscbt_proctor_user_idscbt_grader_user_ids- remedial config
- grade mapping:
grade_component_type,grade_component_weight,auto_publish_to_gradebook
FE exam forms and detail views use these fields consistently.
Status: OK.
6.6 Participant and Attempt¶
BE participant supports:
learning: linked student participantindependent: internal student or external participantadmission: admission applicant participant from SPMB bridge
BE attempt supports:
- attempt lifecycle: draft -> in_progress -> submitted -> graded
- per-attempt public access token for independent external public runner
- persistent question order
- answer upsert
- auto-scoring objective questions
- manual grading for essay/short answer
- score sync to
op.exam.attendeesor admission slot
FE runner and grading consume this contract correctly.
Status: OK.
7. Context Sync¶
7.1 Learning¶
Purpose:
- Ulangan/ujian kelas internal.
FE entry points:
/faculty/assignments/exam-create/faculty/exam/detail/:id/student/cbt/student/cbt/runner/:exam_id
BE contract:
exam_context = "learning"batch_idandsubject_idrequiredquestion_set_idmakes exam CBT- learning CBT requires
grade_component_type - participant source is
op.exam.attendees - scores sync to
op.exam.attendees - optional publish to
academic.grade
Status: OK.
7.2 Independent¶
Purpose:
- Tryout, olympiad, public/external CBT outside kelas reguler.
FE entry points:
/faculty/cbt/exams/faculty/cbt/exams/:examId/cbt/public/cbt/public/runner/:exam_id
BE contract:
exam_context = "independent"- public runner only allowed for independent external participants
- public runner requires
participant_code - participant manual/import allowed
- results report only, no academic grade publish
Status: mostly OK. Public route feature flag gap noted below.
7.3 Admission¶
Purpose:
- Tes masuk SPMB.
FE entry points:
/admin/spmb/cbt/exams/admin-staff/admissions/cbt/exams/spmb/cbt/spmb/cbt/runner/:exam_idsrc/views/AdmissionManagement/TestSchedule.vuemappingcbt_exam_id
BE contract:
exam_context = "admission"- internal SPMB CBT exam list forced to admission context
- SPMB schedule can only link CBT exam context admission
- schedule sync creates or updates
scola.cbt.participantidentityadmission - authenticated admission portal user can start runner without token
- score sync updates
scola.admission.test.slot
Status: OK.
8. Access and RBAC Sync¶
8.1 Frontend Capability and Feature Flag¶
Main FE capability mapping:
academics.question_bank.manageacademics.cbt_exams.manageacademics.cbt_exams.sitacademics.cbt_proctor.manageacademics.cbt_grading.manageacademics.exam_results.viewadmissions.cbt_author.manageadmissions.cbt_proctor.manageadmissions.cbt_grade.manage
Main FE feature flag:
scola_cbt
Tests already cover several contracts:
- CBT teacher routes carry
scola_cbt. - Student and SPMB CBT routes carry
scola_cbt. - Internal SPMB CBT routes are admission-scoped.
- Generic teacher does not get proctor route.
- Teacher grading route stays reachable via exam management capability.
- Admin CBT settings is blocked when
scola_cbtis disabled.
8.2 Backend Groups and Access¶
Relevant BE groups:
scola_cbt.group_cbt_managerscola_cbt.group_cbt_exam_creatorscola_cbt.group_cbt_proctorscola_cbt.group_cbt_graderscola_cbt.group_cbt_auditor
Admission bridge:
scola_core.group_scola_admin_staffimplies CBT exam creator and proctor.scola_core.group_scola_head_adminimplies CBT manager.
Controller-level access:
- Question read/create: manager, exam creator, faculty-like user, admission manager for read.
- Question set update/delete: manager or owner; delete blocked when active exam uses the set.
- Exam create: faculty-like, CBT manager, CBT exam creator, OpenEduCat admin.
- Existing exam manage: manager/admin, admission manager for admission exams, assigned author, owner/creator, faculty scope.
- Proctor: manager/admin, assigned proctor, admission manager.
- Grading: manager, grader, assigned grader, admission manager, faculty managing exam.
Status: mostly OK.
8.3 Access Notes¶
- Generic teacher can create and manage own learning CBT and question bank via FE capability and BE faculty-like/ownership checks.
- Generic teacher cannot proctor unless explicitly assigned or given proctor group. FE and BE align.
- SPMB admin/admin_staff routes use admissions CBT capabilities; BE group bridge makes these users eligible on CBT controllers.
- Academic grading route uses
academics.cbt_exams.manage, whileacademics.cbt_grading.manageexists in registry but is not the teacher route gate. This is intentional per current test, but should be documented as route-policy decision.
9. Policy Sync¶
9.1 Fields Synced From Settings to Backend¶
FE CBTSettings.vue can read/update policy fields:
default_duration_minutesgrace_period_minutesauto_submit_on_timeoutallow_back_navigationallow_question_flagshow_question_navigatorflag_tab_switchflag_blur_eventflag_paste_eventblock_multi_loginfullscreen_requiredmax_flag_countshow_score_immediatelyallow_negative_markingnegative_mark_percentagemax_attemptsretake_score_policyautosave_interval_secondsautosave_on_changeautosave_debounce_ms
BE scola.cbt.policy stores and returns those fields through /api/scola_cbt/policy/get and runner policy payload.
9.2 Fields Used by FE Runner¶
FE runner actively uses:
show_question_navigatorallow_question_flagallow_back_navigationfullscreen_requiredflag_tab_switchflag_blur_eventflag_paste_eventautosave_interval_secondsautosave_on_changeautosave_debounce_ms
9.3 Fields Used by BE Runtime¶
BE runtime actively uses:
max_flag_countfor integrity threshold.cbt_durationfrom exam, not policy default, to calculate attempt deadline.exam.max_attemptsfor retake count.exam.cbt_show_resultto decide whether submit response includes score.
9.4 Policy Gaps¶
The following policy fields are stored and exposed, but no complete runtime enforcement was found in the recon:
| Field | Observed state | Risk |
|---|---|---|
grace_period_minutes |
Stored and returned, but runner time gate/deadline uses strict exam.end_time and attempt end_time. |
Admin may expect grace period to allow late sync/start, but runtime does not honor it. |
block_multi_login |
Stored and returned, but no active attempt/session blocking was found in runner start/state. | Admin may enable multi-login blocking without effect. |
allow_negative_marking |
Stored and returned. | Scoring always gives zero for wrong single-choice answer and partial non-negative score for mcq_multi. |
negative_mark_percentage |
Stored and returned. | No deduction path observed in _auto_score. |
show_score_immediately |
Stored and returned. | Submit response uses exam.cbt_show_result, not policy show_score_immediately. |
Recommendation:
- Either implement enforcement in BE runtime, or hide/label these settings as "planned/not enforced" until implemented.
- Prefer BE enforcement for anything affecting scoring, timing, and security.
10. SPMB CBT Bridge Sync¶
10.1 FE Flow¶
src/views/AdmissionManagement/TestSchedule.vue:
- Loads CBT exams with filter
{ exam_context: "admission" }. - Lets admin map
form.cbt_exam_id. - Persists mapping through
/api/SPMB/test-schedules/<id>/update. - Runs sync through
/api/SPMB/test-schedules/<id>/sync-cbtwith modeparticipants,results, orboth.
src/services/spmb/spmbAdminTestSchedule.service.js:
- Normalizes
cbt_exam_id,cbt_exam_context,cbt_participant_count, andcbt_last_synced_at. - Serializes
cbt_exam_idon create/update.
10.2 BE Flow¶
scola_admission_assessment:
- Adds
cbt_exam_idto admission test schedule. - Domain and constraint require
is_cbt = Trueandexam_context = "admission". - Creates or links
scola.cbt.participantwithidentity_type = "admission". - Syncs submitted/graded attempt score back to admission test slot.
Status: OK.
10.3 Admission Runner¶
FE /spmb/cbt/runner/:exam_id reuses CBTRunner.vue with route meta cbtContext: "admission".
BE runner:
- Allows authenticated admission context runner to start without token.
- Resolves participant by
user_idoradmission_id.portal_user_id. - Rejects duplicate participant mapping for same user/exam.
Status: OK.
11. Findings and Recommendations¶
High¶
H1. CBT policy fields are configurable but not fully enforced¶
Affected fields:
grace_period_minutesblock_multi_loginallow_negative_markingnegative_mark_percentageshow_score_immediately
Impact:
- Admin UI implies runtime behavior that may not happen.
- Scoring and timing policy are high-trust features; mismatch can affect exam fairness.
Recommendation:
- Implement BE enforcement and add BE tests.
- Add FE contract tests or UI copy only after BE behavior is explicit.
- For
show_score_immediately, decide whether exam-levelcbt_show_resultoverrides policy or policy should be source of truth.
Medium¶
M1. CSV/XLSX import does not support true multi-correct mcq_multi¶
Impact:
- Manual authoring supports multiple correct options.
- Runner/scoring supports multiple correct options.
- Import template and backend import only support one correct answer A-E.
Recommendation:
- Extend import contract to allow values like
A,CorA|C. - Add backend parser tests for
mcq_multi. - Update FE sample template and validation copy.
M2. Public CBT routes do not carry FE feature flag metadata¶
Routes:
/cbt/public/cbt/public/runner/:exam_id
Impact:
- Authenticated CBT routes fail closed via FE feature flag.
- Public CBT routes rely on backend/module availability rather than route guard metadata.
Recommendation:
- Add
featureFlag: "scola_cbt"to public route meta if public routes should also fail closed client-side. - Add route guard test for public CBT route behavior.
M3. Pendaftar admission detail parent route is gated by scola_cbt¶
Route:
/spmb/admission/:id(\\d+)
Impact:
- Detail pendaftar includes non-CBT tabs such as documents, achievements, and enrollment.
- With
featureFlag: "scola_cbt"on parent route, tenants without CBT may be blocked from non-CBT admission detail.
Recommendation:
- Move
scola_cbtgating to CBT-specific child/entry points only, unless the whole admission detail truly depends on CBT. - Add router test for pendaftar admission detail with
scola_cbt = false.
M4. Academic grading capability naming is broader than route gate¶
Observed:
academics.cbt_grading.manageexists.- Teacher grading workspace route uses
academics.cbt_exams.manage. - Existing test intentionally enforces this.
Impact:
- Not a functional bug today, but can confuse future RBAC changes.
Recommendation:
- Document this as intentional, or split route behavior:
- teacher-managed exam grading via
academics.cbt_exams.manage - standalone grader access via
academics.cbt_grading.manage
Low¶
L1. Question set sort is local after paginated fetch¶
Impact:
- Sorting in FE may only reorder current page, not full dataset.
Recommendation:
- If global sorting is desired, expose
orderinlistQuestionSetsservice and pass it to BE.
L2. Question delete endpoint exists but is not exposed in primary FE¶
Impact:
- Service has
deleteQuestion, BE endpoint exists, but BE restricts delete to CBT Manager. - Main question set detail UX does not expose item delete.
Recommendation:
- Either keep as manager-only backend function, or add explicit manager-only UI if needed.
L3. Tag listing is more permissive than question authoring¶
Impact:
- BE
tag/listis available to authenticated users, while create/update question requires stronger question access.
Recommendation:
- Accept if tags are considered harmless metadata lookup, or align access check with question read.
12. Current Test Coverage Relevant to Sync¶
Frontend¶
tests/menu/cbtMenuRouteRbacSync.spec.js- teacher/student/SPMB CBT route/menu alignment
- internal SPMB CBT route admission scoping
tests/menu/teacherCbtFeatureFlag.spec.js- teacher CBT routes enforce
scola_cbt - learning exam create/detail route fail closed for LMS-only tenants
tests/menu/teacherCapabilityCoverage.spec.js- generic teacher cannot access proctor route
- grading workspace stays reachable for teacher-managed exams
tests/menu/routerFeatureFlagGuard.spec.js- admin CBT route blocked when
scola_cbtdisabled
Backend¶
scola_cbt/tests/test_question_set_contract_unit.py- question set access, owner/manager update/delete, delete guard for active exams
scola_cbt/tests/test_exam_contract_unit.py- learning CBT requires
grade_component_type - update derives
is_cbtfromquestion_set_id - standard learning exam can exist without question set
scola_cbt/tests/test_runner_admission_unit.py- authenticated admission runner can start without token
scola_cbt/tests/test_runner_timeout_unit.py- expired sync auto-submits and does not write late answer
scola_cbt/tests/test_grading_contract_unit.py- grading payload and access contract
scola_cbt/tests/test_proctor_structure_unit.py- proctor route structure
scola_cbt/tests/test_report_structure_unit.py- report/export route structure
13. Suggested Next Steps¶
- Decide source of truth for score visibility:
- exam field
cbt_show_result - policy field
show_score_immediately -
or policy default with exam override
-
Implement or hide unenforced policy fields:
- grace period
- multi-login blocking
-
negative marking
-
Extend question import for
mcq_multi: - parse
A,CorA|C - validate multiple correct answers
- update FE sample rows
-
add BE unit tests
-
Tighten FE route gating:
- add
scola_cbtfeature flag to public CBT routes if desired -
move SPMB admission detail CBT flag to CBT-only child/entry routes
-
Clarify grading capability policy:
- keep current
academics.cbt_exams.manageintentionally -
or introduce standalone grader route/capability behavior
-
Add contract tests for policy semantics:
- negative marking scoring
- grace period timeout behavior
- multi-login block behavior
- score visibility response behavior