QTI 3.0 規格に基づいたオンラインテストプラットフォームの開発の試行です。 この試行にて確認したいことはほぼ達成したので、今後更新される可能性はかなり低いです。
| サービス | URL |
|---|---|
| Basic Run | https://qti-mock.shumi.dev/basic |
| Playground | https://qti-mock.shumi.dev/playground |
| Web (Next.js) | https://qti-mock.shumi.dev/ |
| Player (Vue) | https://qti3-player.shumi.dev/ |
本プロジェクトは、IMS Global の QTI (Question and Test Interoperability) 3.0 規格に準拠したアセスメントアイテムを表示・採点するシステムです。
┌─────────────────────────────────────────────────────────────┐
│ Next.js (packages/web) https://qti-mock.shumi.dev │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ BasicRunInProgress (iframe + サイドバー) │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ Vue Player (packages/qti-player) │ │ │
│ │ │ https://qti3-player.shumi.dev │ │ │
│ │ │ - QTI XML の読み込み・表示 │ │ │
│ │ │ - 採点処理 │ │ │
│ │ │ - 回答内容・所要時間の抽出 │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ postMessage │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ BasicRunResults │ │
│ │ - 問題別結果テーブル(回答・時間・スコア) │ │
│ │ - 正答率・総合成績表示 │ │
│ │ - 未採点問題(外部採点)の識別 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
qti-mock/
├── packages/
│ ├── qti-player/ # Vue 3 QTI Player
│ │ ├── src/
│ │ │ ├── App.vue # メインコンポーネント(採点・回答抽出)
│ │ │ ├── main.js # エントリーポイント(Analytics含む)
│ │ │ └── composables/
│ │ │ ├── useItemLoader.js # XML読み込み
│ │ │ └── useResultSubmit.js # 結果送信
│ │ └── package.json
│ │
│ └── web/ # Next.js Web Application
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx # ルート(/home へリダイレクト)
│ │ │ ├── home/page.tsx # ホームページ
│ │ │ ├── basic/page.tsx # Basic Run ページ
│ │ │ └── playground/page.tsx # Playground ページ
│ │ ├── components/
│ │ │ ├── QtiPlayerFrame/ # iframe wrapper
│ │ │ ├── navigation/ # ナビゲーション(サイドバー等)
│ │ │ ├── basic-run/ # Basic Run 関連コンポーネント
│ │ │ │ ├── BasicRunInProgress.tsx # テスト実行画面
│ │ │ │ ├── BasicRunResults.tsx # 結果表示画面
│ │ │ │ └── BasicRunInitialScreen.tsx # 開始画面
│ │ │ └── playground/ # Playground コンポーネント
│ │ │ └── PlaygroundPage.tsx
│ │ └── types/
│ │ └── test.ts # 型定義(ItemResult等)
│ ├── public/
│ │ ├── items-h/ # QTI XMLファイル(横書き)
│ │ └── items-v/ # QTI XMLファイル(縦書き)
│ └── package.json
│
├── qti-generator/ # AI問題生成ツール(Python)
│ ├── .env # 環境変数(共通)
│ ├── .env.example # 環境変数テンプレート
│ ├── ai-choice/ # 4択問題生成
│ │ ├── src/main.py # エントリーポイント
│ │ ├── config/settings.py
│ │ └── README.md
│ └── ai-text/ # 記述式問題生成
│ ├── src/main.py # エントリーポイント
│ ├── config/settings.py
│ └── README.md
│
├── package.json # ルート (npm workspaces)
├── turbo.json # Turborepo 設定
├── vercel.json # Vercel設定(東京リージョン)
└── CLAUDE.md # Claude Code ガイド
| パッケージ | 技術 | 説明 |
|---|---|---|
| qti-player | Vue 3 + Vite 7 | QTI 3.0 アイテムのレンダリング・採点 |
| web | Next.js 16 + React 19 + TypeScript | アプリケーション本体 |
| qti-generator | Python 3.10+ | AI問題生成ツール(Claude API使用) |
| - | Turborepo | モノレポ管理 |
| - | Vercel | ホスティング(東京リージョン) |
- qti3-item-player-vue3 - QTI 3.0 プレイヤーコンポーネント
- Material UI - UIコンポーネント(Tooltip, Table等)
- Tailwind CSS 4 - ユーティリティファーストCSS
- Vercel Analytics - ユーザー行動分析(両パッケージで有効)
- Node.js 20.x 以上
- npm 11.x 以上
# リポジトリをクローン
git clone https://github.com/kergee3/qti-mock.git
cd qti-mock
# 依存関係をインストール
npm install# 両方のパッケージを同時に起動
npm run dev
# または個別に起動
npm run dev:player # http://localhost:5173
npm run dev:web # http://localhost:3000- ブラウザで http://localhost:3000/basic を開く
- プリセット問題でテストを実行(各種インタラクション)
- 問題番号にホバーすると、タイトルとインタラクションタイプを表示
- 全問完了後、結果画面で回答内容・所要時間・スコアを確認
または http://localhost:3000/playground で QTI XML を直接入力してテストを実行
| インタラクション | 説明 | サンプル |
|---|---|---|
| choiceInteraction | 単一/複数選択 | choice-item-001.xml |
| inlineChoiceInteraction | インライン選択 | inline-choice-item-001.xml |
| matchInteraction | マッチング | match-item-001.xml |
| orderInteraction | 並べ替え | order-item-001.xml |
| textEntryInteraction | テキスト入力 | text-entry-item-001.xml |
| extendedTextInteraction | 長文テキスト入力(外部採点) | vertical-ext-text-27.xml |
| graphicChoiceInteraction | 画像選択(ホットスポット) | graphic-choice-item-001.xml |
プリセット問題でテストを体験できるモード。選択問題、並べ替え問題、テキスト入力問題などが含まれています。
QTI XML を直接入力またはドラッグ&ドロップで自由にテストを実行できるモード。
- 左サイドバーに問題番号ボタンを表示
- 問題番号にホバーでタイトル・インタラクションタイプをTooltip表示
- 回答状況に応じた色分け(青: 現在、緑: 正解、赤: 不正解、オレンジ: 未採点、グレー: 未回答)
- 問題番号下に区切り線を引いて終了ボタンを配置
- 問題別結果テーブル(問題番号、タイトル、回答、所要時間、結果)
- 回答列は長文の場合省略表示(...)、Tooltipで全文表示
- 所要時間(秒)を独立した列で表示
- 正答率・総合スコアの表示
- 外部採点問題の識別と未採点数の表示
- responseVariables から回答内容を抽出(numAttempts除外)
- duration(所要時間)を別フィールドとして抽出
- HTMLタグの除去(
<p>テキスト</p>→テキスト) - postMessage で親ウィンドウに結果を送信
- クライアントサイドで結果を累積管理
- サーバーレス環境(Vercel Functions)に対応
- セッションIDによる結果の追跡
- 環境変数による動的オリジン設定
- 開発環境・本番環境の両対応
学習指導要領LOD(jp-cos.github.io)のデータを基に、Claude APIを使用してQTI 3.0形式の問題を自動生成するPythonツールです。
cd qti-generator/ai-choice
python src/main.py --subject 社会_政治 --count 10cd qti-generator/ai-text
python src/main.py --subject 社会_政治 --count 5| 科目 | 分野 | コード |
|---|---|---|
| 社会 | 政治 | 8220263100000000 |
| 社会 | 歴史 | 8220263200000000 |
| 社会 | 国際 | 8220263300000000 |
| 理科 | 物質・エネルギー | 8260263100000000 |
| 理科 | 生命・地球 | 8260263200000000 |
詳細は各ディレクトリの README.md を参照してください。
- モノレポ構造への変換
- Vue Player の URL駆動方式対応
- Next.js アプリケーション構築
- iframe 埋め込みコンポーネント
- 結果受信 API (/api/results)
- CORS 設定
- 複数問題のシーケンス管理
- 成績サマリー表示
- Vercelデプロイ
- 本番環境の構築
- CORS設定の本番対応
- カスタムドメイン設定(shumi.dev)
- 東京リージョン(hnd1)設定
- Vercel Web Analytics 導入
- テスト実行画面のサイドバー改善(問題番号Tooltip、終了ボタン配置)
- 結果画面の回答・所要時間の分離表示
- 長文回答の省略表示とTooltip
- 回答からHTMLタグ除去
- 各種インタラクションタイプのサポート拡充
-
Phase 3: ETL構築
- QTI XMLの変換・インポート機能
- データベース連携(問題・結果の永続化)
-
Phase 5: 本番運用準備
- エラー監視(Sentry等)
- パフォーマンスモニタリング
- ドキュメント整備
| 変数名 | 説明 | 例 |
|---|---|---|
NEXT_PUBLIC_APP_URL |
Webアプリの公開URL | https://qti-mock.shumi.dev |
NEXT_PUBLIC_PLAYER_URL |
Playerの公開URL | https://qti3-player.shumi.dev |
PLAYER_URL |
CORS許可オリジン | https://qti3-player.shumi.dev |
npm run dev # 全パッケージの開発サーバー起動
npm run dev:player # Vue Player のみ起動
npm run dev:web # Next.js のみ起動
npm run build # 全パッケージをビルド
npm run lint # リント実行MIT
- QTI 3.0 Specification
- qti3-item-player-vue3
- 学習指導要領LOD - AI問題生成のデータソース
- Claude API - AI問題生成に使用
- Turborepo Documentation
- Next.js Documentation
- Vercel Documentation