Skip to content

Repository files navigation

Go(lang)FlutterCAT

Computer adaptive testing (CAT) platform with a Go backend (IRT engine + REST API), an embedded server-rendered HTML frontend (gomponents + htmx), and a Flutter frontend.

When provided, the scoring algorithm marginalizes against a PairwiseOrdinalStacking model for performing "imputation" on unobserved item responses. Doing so keeps the score consistent without assuming that the IRT model is well-specified.

Item Selection Strategies

The following selectors are included:

  • Fisher information maximization (greedy)
  • Posterior variance minization (greedy)
  • "Global information" (greedy)
  • KL Divergence (greedy)
  • Entropy (stochastic)

The stochastic entropy selector was derived in arXiv:2504.15543 for having the frequentist item sampling statistics correspond to performing Bayesian model averaging in ability space.

GoFlutterCAT home screen

Assessment in progress

Results page

Quick Start

Prerequisites

  • Go 1.25+
  • Flutter SDK 3.11+

Start the Server

go run . server

On startup the server prints:

 Frontend: http://localhost:19401/
API Docs: http://localhost:19401/docs
Example: curl http://localhost:19401/instruments

The embedded HTML frontend is served at / — no separate build step, no Node.js, no Flutter SDK required. Just a single Go binary.

Configuration is loaded from backend-golang/config/config-default.yml. Override with environment variables:

PORT=8080 go run . server # change the listen port
APP_ENV=production go run . server # load production config

Flutter Frontend (optional)

A richer Flutter frontend is also available:

cd frontend-flutter
flutter pub get
flutter run -d chrome # web
flutter run # default device

To point the Flutter frontend at a different backend:

flutter run --dart-define=API_BASE_URL=http://192.168.1.10:19401

Bundled Instruments

The backend ships with five embedded instruments — no extra configuration needed:

InstrumentItemsResponse FormatScale
Right-Wing Authoritarianism (RWA)229-point LikertRight-Wing Authoritarianism
Duckworth Grit Scale125-point LikertGrit
Narcissistic Personality Inventory (NPI)40Binary forced-choiceNarcissism
Taylor Manifest Anxiety Scale (TMA)50True/FalseAnxiety
Woodworth Psychoneurotic Inventory (WPI)116Yes/NoPsychoneurosis

Each instrument includes a MICE Bayesian LOO imputation model for Rao-Blackwellized scoring under non-ignorable missingness. The frontend displays an instrument selector on startup.

API Endpoints

MethodPathDescription
GET/instrumentsList available instruments
GET/assessment?instrument=XAssessment metadata (name, description, scales, CAT config)
POST/sessionCreate a new CAT session ({"instrument": "grit"})
GET/sessionList active session IDs
GET/{sid}/itemGet next item (adaptive selection across scales)
GET/{sid}/{scale}/itemGet next item for a specific scale
POST/{sid}/responseSubmit a response ({"item_name": "Q1", "value": 3})
GET/{sid}Get session summary with scores
DELETE/{sid}Deactivate session

Defining a New Assessment

An assessment consists of item files (the questions), scale definitions, and a config entry that ties them together.

1. Item Files

Each item is a JSON file with this structure:

{
"item": "PHQ1",
"question": "Over the last 2 weeks, how often have you had little interest or pleasure in doing things?",
"responses": {
"0": {"text": "Not at all", "value": 0},
"1": {"text": "Several days", "value": 1},
"2": {"text": "More than half the days", "value": 2},
"3": {"text": "Nearly every day", "value": 3}
},
"scales": {
"depression": {
"discrimination": 2.1,
"difficulties": [-1.5, 0.3, 1.8]
}
}
}

Key fields:

  • item: Unique item identifier
  • question: The text shown to the respondent
  • responses: Map of response options. Each has text (label) and value (numeric score). Use value 0 for a skip option.
  • scales: IRT calibration parameters per scale this item loads on.
    • discrimination: How well this item differentiates between ability levels (higher = more discriminating)
    • difficulties: GRM category boundary thresholds. For a k-point response scale, provide k-1 difficulty values in ascending order.

An item can load on multiple scales (cross-loading) by having multiple entries in the scales map.

2. Scale Definitions

Scales are configured in config-default.yml under assessment.scales:

assessment:
scales:
depression:
name: depressiondisplayName: "Depression Severity"loc: 0# prior mean (usually 0)scale: 1# prior standard deviation (usually 1)anxiety:
name: anxietydisplayName: "Anxiety Severity"loc: 0scale: 1

Alternatively, scales can be loaded from a JSON file (see scalesFile config below), or auto-discovered from item calibrations if no scales are configured.

3. Configuration

Add or modify the assessment section in your config YAML:

assessment:
name: "PHQ-CAT"description: "Computer-adaptive depression and anxiety screening."source: directory # "embedded" for built-in RWAS, "directory" for external itemsitemsDir: /path/to/items # directory containing item JSON filesscalesFile: ""# optional: path to scales JSON filescales:
depression:
name: depressiondisplayName: "Depression"loc: 0scale: 1anxiety:
name: anxietydisplayName: "Anxiety"loc: 0scale: 1

Source options:

  • embedded (default): Uses the built-in RWAS item pool. Set variant to factorized or autoencoded.
  • directory: Loads item JSON files from the path specified by itemsDir. All .json files in that directory are loaded.

4. CAT Stopping Rules

cat:
stoppingStd: 0.33# stop when posterior SD drops below thisstoppingNumItems: 12# max items per scaleminimumNumItems: 4# min items before stopping is allowed

Putting It Together

  1. Prepare your item JSON files (one per item) in a directory
  2. Add the assessment config to your YAML config file
  3. Start the backend — it will load your items and expose them via the API
  4. The Flutter frontend automatically picks up the assessment name, description, and scale labels from GET /assessment

Project Structure

gofluttercat/
├── backend-golang/
│ ├── cmd/ # CLI entry points
│ ├── config/ # Configuration (Viper)
│ ├── pkg/
│ │ ├── irtcat/ # Core IRT-CAT engine
│ │ │ ├── grm.go # Graded Response Model
│ │ │ ├── score.go # Bayesian scoring
│ │ │ ├── item.go # Item data structures
│ │ │ ├── session.go # Session state (Badger DB)
│ │ │ └── ...
│ │ ├── frontend/ # Embedded HTML frontend (gomponents + htmx)
│ │ │ ├── handler.go # HTTP handlers for frontend routes
│ │ │ ├── layout.go # Page shell (Pico CSS + htmx CDN)
│ │ │ ├── home.go # Instrument selection
│ │ │ ├── assessment.go # Question card, choice buttons
│ │ │ ├── results.go # Score cards, forest plot (inline SVG)
│ │ │ └── styles.go # Custom CSS
│ │ ├── web/ # HTTP server and routes
│ │ │ ├── server.go # App init, multi-instrument loading
│ │ │ ├── routes.go # Route definitions (usecase pattern → OpenAPI)
│ │ │ └── handlers/ # Request handlers
│ │ ├── math/ # Math utilities
│ │ ├── imputation/ # MICE Bayesian LOO imputation
│ │ ├── simulation/ # Monte Carlo CAT session simulation
│ │ └── {rwa,grit,npi, # Per-instrument loaders
│ │ tma,wpi}/
│ ├── rwa/ # Embedded RWA items + imputation model
│ ├── grit/ # Embedded Grit items + imputation model
│ ├── npi/ # Embedded NPI items + imputation model
│ ├── tma/ # Embedded TMA items + imputation model
│ └── wpi/ # Embedded WPI items + imputation model
├── frontend-flutter/
│ └── lib/
│ ├── models/ # Data models (session, item, score, instrument)
│ ├── services/ # API client
│ ├── providers/ # State management (Provider)
│ ├── screens/ # Home, Assessment, Results
│ └── widgets/ # Likert scale, score gauge, etc.
├── python/ # Item extraction & model conversion scripts
└── static/ # Favicon

Testing

# Backend
go test ./...
# Frontendcd frontend-flutter && flutter test

License

BSD — see source file headers for full notice. Courtesy of the U.S. National Institutes of Health Clinical Center.

About

Computer adaptive testing (CAT) administration

Resources

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages