0%
balqalam Logo
balqalam
FeaturesCommunityPricingDocumentation
Login
  • Introduction
  • Help center
  • Common questions
  • Get Started
  • Localhost
  • Architecture
  • Structure
  • Pattern
  • Page
  • Layout
  • Content
  • Types
  • Config
  • Actions
  • Queries
  • Authorization
  • Validation
  • Form
  • Table
  • Detail
  • Card
  • Util
  • Hooks
  • List Params
  • Views
  • README.md
  • ISSUE.md
  • Technology Stack
  • Database
  • File
  • CDN Assets
  • Entry Points
  • Dashboard
  • Authentication
  • Credentials
  • OAuth
  • Flow Diagrams
  • Multi-Tenancy
  • Offline
  • Onboarding
  • Onboarding Videos
  • Add Values
  • Admission
  • Application
  • Attendance
  • Compliance
  • Profile
  • Exams
  • Exam Wizard
  • Timetable
  • Classrooms
  • Notifications
  • Conference
  • LMS (Lumos)
  • Finance
  • Fee Management
  • Invoice
  • Wallet
  • Salary
  • Payroll
  • Timesheet
  • Expenses
  • Budget
  • Receipt
  • Accounts
  • Banking
  • Reports
  • Dashboard
  • Permissions
  • Messages
  • Integration Flow
  • Provision
  • AI Document Processing
  • Document Intelligence
  • Internationalization
  • Translation
  • Translation Guide
  • Icons
  • Docs Factory
  • Inspiration
  • Listings
  • Teachers
  • Students
  • Catalog
  • Library
  • Contributing
  • Code of conduct
  • GitHub Workflow
  • Database Seeds
  • Database Safety
  • Test Accounts
  • Bulk import
  • Playwright
  • Prettier
  • Block Rebound
  • Transportation

Exams

PreviousNext

End-to-end examination — question bank, paper generation, digital taking, AI marking, results, certificates.

The exams module covers the full lifecycle: question authoring, paper printing, digital taking, AI-assisted marking, analytics, certificates, and progress tracking. 18 sub-modules, 40+ routes, ~80 component files.

On a phone

Below the md breakpoint the exams overview is composed per role — a banner for the next exam, a figures panel, app-tile doors, and dated rows for upcoming exams and results — and the authoring pages (generate, catalog, question bank, certificates) show grey cards two across with pill buttons. Tablets and desktops are unchanged; the exams data tables stay tables.

Sub-modules

Sub-modulePurpose
Question bankStore and manage questions by subject, type, difficulty, Bloom level
PsychometricsItem analysis, discrimination index, distractor effectiveness
TemplatesReusable blueprints with question distribution rules
CatalogBrowse and import shared exam templates
BlueprintsRegional exam structures (national presets)
Auto-generationGenerate exams from templates with smart selection
VersionsMultiple versions with shuffled questions and contribution tracking
AI question genGenerate questions from curriculum (beta)
ManagementCRUD, scheduling, conflict detection, analytics
Paper generationPDF papers with composable template system
CompositionVariant-based slot composition
Regional presetsCountry-specific formatting (SD, SA, US, MENA)
Digital takingOnline exam player with proctoring
Quick examsLightweight teacher quizzes with instant take
Auto-markingMCQ, T/F, fill-in-blank
AI gradingEssays and short answers
OCRHandwritten answers via GPT-4 Vision
Bulk markingGrade entire exams at once, question-based view
Results & analyticsPerformance analysis, batch PDF, CSV export
CertificatesConfigurable templates with QR verification
GradingBoundaries, CGPA, retakes
ProgressSchedules and monitoring
Mock examsPractice exams
Quiz gamesGamified quizzes

Question bank

7 question types, 4 auto-gradable, 2 AI-assisted.

TypeAuto-gradableTemplate
Multiple choiceYtemplates/multiple-choice/
True / falseYtemplates/true-false/
Fill in blankYtemplates/fill-blank/
Short answerAI-assistedtemplates/short-answer/
EssayAI-assistedtemplates/essay/
MatchingYtemplates/matching/
OrderingYtemplates/ordering/

qbank/analytics/ provides discrimination index, distractor analysis, difficulty calibration, success rate, usage frequency. qbank/catalog-tab.tsx browses shared questions.

CSV bulk import:

questionText,questionType,difficulty,bloomLevel,points,options,sampleAnswer,tags
"What is 2+2?",MULTIPLE_CHOICE,EASY,REMEMBER,1,"[{""text"":""4"",""isCorrect"":true},{""text"":""3"",""isCorrect"":false}]","","math,arithmetic"

Create an exam — two modes

The front door (/exams/new) is a single screen offering two creation modes. Both converge on the same downstream — a GeneratedExam with questions → paper preview → schedule — and differ only in how the blueprint and questions are seeded.

ModeRouteSeeds fromSpeed
Adopt a template/exams/generate/catalogA platform-curated, curriculum-aligned ExamTemplate (browse → preview → Adopt)Fastest
Generate with AI/exams/qbank/ai-generateA topic prompt → AI-drafted questions into your bank (review before saving)Fast

The in-app "build a template from primitives" wizard was removed — a paper's layout is now an uploaded .docx (see Document templates below), while its question distribution lives on a SchoolExamTemplate blueprint seeded by adopting a catalog blueprint.

Document templates

Schools print on their own mandated stationery, so the paper's layout is a .docx the school uploads — not something built in-app. /exams/templates (staff only: DEVELOPER / ADMIN / TEACHER) manages two categories, Exam papers and Certificates; report-card layouts live at /grades/templates. There is no standalone /documents page.

Author the Word file with {{tag}} placeholders and {{#loop}}…{{/loop}} sections, upload it, and the merge fields are detected automatically and shown back to you. The full vocabulary per category is listed in the upload dialog.

You do not have to start from a blank page: Starter template hands you a real .docx for that category with every tag already correct — edit the styling, keep the tags. Use it.

What the upload checks

Every one of these failures used to surface only once a school printed the paper, so the upload now inspects the file and tells you which it is:

What is wrongWhat happens
A section is opened but never closed (or closed but never opened)Refused — it can never fill. The offending tag is named so you can fix it in Word and upload again
Single braces: {#questions} instead of {{#questions}}Saved, with a warning listing each marker. They print into the document as text and their contents are dropped
A tag this category has no data for — usually a typo, e.g. {{schoolNmae}}Saved, with that tag flagged in the detected list. It fills blank
No tags at allSaved, with a warning. Filling it returns an unchanged copy of your file

Only the first is rejected; the rest are stored so you can still use the template if the warning is a false alarm.

Filling an exam paper

An uploaded exam-paper template is bound to real exam data through Use template, which offers two sources:

SourceWhat you pickWhat happens
An existing examA GeneratedExam that already has questionsFills your layout directly
Build one nowA blueprint + grade (one section or all) + paper title + date + exam typeCreates the SchoolExam + GeneratedExam, auto-selects questions from the bank per the blueprint, fills

Before generating, the dialog shows coverage — every tag detected in your template, marked as either fillable or unsupported — so a mismatch is visible before you download rather than as a blank in the printed paper. Output is .docx; PDF conversion is deferred.

Two ways to lay out the body

The resolver always supplies both, so a template uses whichever fits and the unused one costs nothing:

LoopShape
{{#questions}}…{{/questions}}One flat, continuously numbered list
{{#sections}}…{{/sections}}The same questions grouped by type — "1. Multiple choice", "2. Essay" — each with its own mark total

Inside a section, questions keep the paper-wide {{order}} and also carry {{numberInSection}}, so a sectioned paper and a flat one number identically. Each question exposes {{typeLabel}}, the conditional {{#hasOptions}} / {{#isMcq}} blocks, and {{#answerLines}} — blank ruled writing space sized by question type (essay 8 lines, short answer 3, fill-in-the-blank 1, none for multiple choice). The starter template demonstrates all of it.

When the bank is short

Question selection degrades rather than fails: it fills every distribution slot the bank can cover and records the rest. A blueprint asking for six hard essays against a bank holding none still produces a paper — a short one. The dialog stays open after the download and names the unfilled categories, because the alternative is a teacher discovering a 12-mark paper for a 50-mark exam in the exam hall. Generation only fails outright when the bank yields no question at all, and in that case the newly created exam is rolled back rather than left empty on the schedule.

Generation

RoutePurpose
/exams/generate/templatesDistribution rules per type, difficulty, Bloom
/exams/generate/catalogBrowse and import shared templates
/exams/qbank/ai-generateAI-drafted questions from a topic prompt
/exams/generate/versionsMultiple shuffled versions
/exams/generate/contributionsAuthor tracking
/exams/templatesYour own .docx layouts (upload + fill)

Smart selection: group by type and difficulty, fill distribution, apply Bloom distribution, seeded randomization, prevent duplicates across versions.

Management

RoutePurpose
/exams/newCreate-exam chooser (adopt · AI · from scratch)
/exams/[id]Details + result summary
/exams/[id]/editModify before administration

manage/calendar.tsx — visual schedule. manage/conflict-display.tsx — overlap warnings. manage/analytics-dashboard.tsx — school-wide stats, standards coverage, quick paper-gen, export.

The /exams/manage list (manage/columns.tsx) shows seven columns: Title (row anchor), Grade (the exam's section, else its grade), Subject, Type, When (date · start time · duration, merged into one), Marks, and Status. An exam is set for a grade — one section of it or the whole grade — and a subject;

Paper generation

@react-pdf/renderer-based system with a four-layer composable template:

  1. Atoms (13 in templates/atom/) — alignment-marker, answer-line, blank-slot, bubble, checkbox, divider, field-line, logo-block, option-label, points-badge, question-number, type-label, watermark.

  2. Question sections — one per question type.

  3. Page sections — 6 slots × variants:

    SlotVariants
    Headerstandard, ministry, minimal, bilingual, centered
    Footerstandard, disclaimer, minimal, grading
    Student infostandard, bubble-id, table, photo
    Instructionsstandard, compact, rules, sectioned
    Answer sheetstandard, omr, grid
    Coverstandard, toc, ministry
  4. Composition — ComposableDocument reads CompositionConfig and assembles slot variants.

interface CompositionConfig {
  slots: Record<
    | "header"
    | "footer"
    | "studentInfo"
    | "instructions"
    | "answerSheet"
    | "cover",
    string
  >
  decorations: {
    accentBar: {
      enabled: boolean
      height?: number
      colorKey?: "accent" | "primary"
    }
    watermark: { enabled: boolean; text?: string; opacity?: number }
    frame: { enabled: boolean; outerWidth?: number; innerWidth?: number }
  }
  slotProps: Record<string, unknown>
}

Resolution: blockConfig JSON → regional preset overrides → DEFAULT_COMPOSITION.

paper/composition-panel.tsx provides the variant picker. The four legacy templates (Classic, Modern, Formal, Custom) are deprecated wrappers around ComposableDocument.

PresetHighlights
sd-nationalSudan — ministry header, disclaimer, watermark
sa-nationalSaudi Arabia — ministry, bubble-id, OMR sheets
us-standardUSA standard layout
mena-privateMENA private — bilingual header, custom branding

Each style has a PaperTheme with locale-aware colour, typography, spacing, RTL/LTR direction, and number style (Arabic / Latin).

Digital taking

take/exam-player.tsx is the full-screen runner: per-question nav, countdown timer, auto-save, validated submit. take/proctor-guard.tsx adds device fingerprint, security flag reporting (tab switch, copy / paste, screen capture), retake-penalty calculation.

startExamSession(input: StartExamSessionInput)
autoSaveAnswers(input: AutoSaveAnswersInput)
reportSecurityFlag(input: ReportSecurityFlagInput)
submitExamAnswers(input: SubmitExamAnswersInput)

Quick assessments and progress reports

A quick assessment (exit ticket, poll, warm-up, check-in) is set for an audience like an exam: a grade, one of its sections or the whole grade, and a subject, picked with the shared TeachingScopePicker and checked by resolveTeachingScope. Only teachers and admins write, launch and close them; a student sees and answers only their audience's, and an identified answer lands in the gradebook under the student's section, grade and term. Assessments made before classes were retired keep their class.

A progress-report schedule covers a section, a whole grade, or — with neither — the whole school; deleting the grade or section deletes its schedule rather than widening it. Only teachers and admins manage schedules or run them.

Marking

NOT_STARTED → AUTO_GRADED | AI_GRADED | IN_PROGRESS → REVIEWED → COMPLETED. Override supported at any point.

autoGradeAnswer(studentAnswerId: string)
aiGradeAnswer(studentAnswerId: string)
manualGrade(answerId: string, points: number, feedback?: string)
bulkGradeExam(examId: string, method: GradingMethod)
overrideGrade(resultId: string, newScore: number, reason: string)

OCR uses GPT-4 Vision for handwritten answers. AI grading uses essay rubrics from the question bank.

Results & analytics

/exams/result lists exams with class average, pass rate, grade distribution. Per-exam at /exams/result/[id]/analytics:

  • Class — average, median, mode, std dev, top / bottom performers, distribution chart, trend.
  • Question — discrimination index, distractor analysis, most missed, average time per question.

Batch PDF at /exams/result/[id]/pdf/batch — background processing, multiple result-card templates, ZIP. CSV export for raw data. results/cache-manager.ts caches computed results.

Certificates

StepSurface
Configs/exams/certificates/configs/new, /configs/[id]
Generate (single)per-student form
Generate (batch)batch action over a class
QR verificationpublic at /exams/certificates/verify/[code]
Revocationwith reason tracking
Shareshareable link
createCertificateConfig(input)
updateCertificateConfig(id, input)
generateCertificate(input)
batchGenerateCertificates(input)
verifyCertificate(code) // public
revokeCertificate(id, reason)
shareCertificate(id)

Server actions

ModulePathSurface
Question analyticsqbank/analytics/actions.tsgetQuestionAnalytics, getQuestionPsychometrics
Generationgenerate/actions.tscreateTemplate, updateTemplate, deleteTemplate, generateExam, previewExamGeneration, regenerateExam
Managementmanage/actions.tscreateExam, updateExam, deleteExam, getExams, getExamDetails
Paperpaper/actions/createPaperConfig, updatePaperConfig, generatePaperPDF
Markingmark/actions.tssee above
Resultsresults/actions.tsgetExamResults, getExamAnalytics, generateSinglePDF, generateBatchPDFs, exportResultsToCSV
Quick assessmentsquick/actions/createQuickAssessment, getQuickAssessments, launchQuickAssessment, submitQuickResponse, getQuickAssessmentResults
Gradinggrading/actions.tsgetGradeBoundaries, updateGradeBoundaries, calculateCGPA, manageRetake
Progressprogress/actions.tscreateProgressSchedule, getProgressSchedules, updateProgressSchedule, generateProgressReports

Seed

pnpm db:seed:single exams                    # 70+ questions, 8 templates, 27 sample exams
pnpm db:seed:single qbank                    # extended question bank + psychometrics
pnpm db:seed:single exam-templates   # shared catalog templates
pnpm db:seed:single exam-blueprints          # regional structures (SD, SA, …)

Troubleshooting

SymptomLikely cause
Exam generation failsPool too small for distribution; fix template or add questions
AI grading errorsOPENAI_API_KEY missing, rate limit, missing rubric
PDF paper crashes@react-pdf/renderer missing; missing logo URL; Rubik font unreachable; insufficient memory for batch
Digital exam won't startExam status not IN_PROGRESS; student not enrolled
Proctor guard inactiveProduction HTTPS required
Certificate QR doesn't scanQR_CODE_SECRET missing; certificate revoked; verify route blocked

Automation pipeline

The scoring loop is largely hands-off once questions are in the bank. Objective exams need zero manual marking; exams with subjective questions take one click.

1. Auto-generate from the bank  (exam-wizard-v2 → "Auto-generate from template")
   └─ autoGenerateExamQuestions() selects questions per the template distribution,
      assigns per-question points, writes GeneratedExamQuestion rows, drops the
      stale answer key. (Adopting a catalog exam does the same in one transaction.)

2. Exam opens on schedule  (/api/cron/exam-lifecycle, */15)
   └─ PLANNED → IN_PROGRESS at the scheduled start; IN_PROGRESS → COMPLETED after
      the end window (examDate + endTime + lateSubmitMinutes).

3. Students take + submit  (take route → ExamPlayer)
   └─ getExamForPlayer() loads the proctored runner, resolving the real Student.id
      and stripping isCorrect from options. submitExamSession() instant-grades a
      fully-objective paper on submit → finalizeStudentExam() → ExamResult + Result.

4. Finalize exams with subjective questions  (one click)
   └─ "Auto-mark & publish" → finalizeExamResults({ aiGradeSubjective: true,
      publish: true }): objective auto-mark + AI-grade essay/short-answer, aggregate
      MarkingResult → ExamResult (analytics, certificates) + Result (gradebook),
      notify students.

5. Report cards auto-generate at term end  (/api/cron/term-end-report-cards, daily)
   └─ generateReportCardsCore() aggregates each term's results into ReportCard +
      ReportCardGrade (dedup by examId), plus credit-weighted GPA, rank and
      attendance days. Set-based: the whole cohort in a fixed handful of queries
      (~1s for 970 students), so it finishes inside a cron/action budget.
      Drafts only — does not publish.

6. Admin reviews + publishes
   └─ publishReportCards() → notifies guardians/students; process-report-card-pdfs
      cron renders the PDF out-of-band → S3 → pdfUrl.

7. Official documents  (documents block)
   └─ Upload a .docx template (EXAM_PAPER / CERTIFICATE / REPORT_CARD) — or start from
      the built-in Starter template — and fill it with real data via "Generate (my
      template)" (generateFromDefaultTemplate). On /grades/reports, "Generate all"
      fills the whole filtered term at once (generateFromDefaultTemplateBulk),
      chunked 50 cards per zip.

8. Reminders  (/api/cron/exam-reminders)
   └─ sweeps upcoming exams, dispatches advance notifications to students + teachers.

Key invariant: grades/lib/gradebook.ts is the single write path for ALL automated scoring surfaces (exams, quick assessments, lumos lesson quizzes). It is not "use server" — import it from server actions.

Deferred: a fully session-less AI-finalize-at-close cron. The marking stack (getOrCreateAnswerKey, batchAutoGradeWithKey, batchAIGrade) derives schoolId from auth(), so a timer can't drive it until cron-callable cores are extracted — until then, subjective exams finalize with the one-click "Auto-mark & publish" button.

See also

  • Exam wizard — template + generation + cert wizards
  • Competitors → assessment tools — the ~25 exam-creation tools that shaped the 3-mode front door
  • Multi-tenancy — RBAC + scoping
ProfileExam Wizard

On This Page

On a phoneSub-modulesQuestion bankCreate an exam — two modesDocument templatesWhat the upload checksFilling an exam paperTwo ways to lay out the bodyWhen the bank is shortGenerationManagementPaper generationDigital takingQuick assessments and progress reportsMarkingResults & analyticsCertificatesServer actionsSeedTroubleshootingAutomation pipelineSee also

Built by Databayt ·

Welcome to balqalam.

A great journey is about to begin.