ナレッジベース

糖化アンケート管理システム — 全ツール概要・アルゴリズム・データ構成

システム全体像

糖化アンケートOCR照合システムは、歯科医院で実施する糖化関連アンケートの回収・OCR読み取り・照合・データ管理を行うシステムです。3つのプロジェクトで構成されています。

touka-survey (Vercel) → アンケート用紙作成・QRコード生成 ↓ QRコード付きPDFを印刷・配布 test-OCR (ローカルPython) → スキャンPDFのOCR処理・照合 ↓ 照合済みデータをGASへ送信 touka-tools (GitHub Pages) → Web管理ダッシュボード(29ツール) ↓ Google Spreadsheet + Drive → 共通データストア

データフロー(2グループ体制)

Group A(QRコード組): 患者がQRコードからGoogleフォームに回答→スプレッドシートに自動保存。PDF紐づけツールでスキャンPDFと紐づけ。

Group B(紙回答組): 紙アンケートをスキャン→OCR処理→照合→スプレッドシートに送信。

データストア

スプレッドシート内容特徴
メイン(回答データ)Googleフォーム+OCRからの回答患者1人=1行、No.で連番管理
検体PpP値シートPEN/PYD/PpP値データ同一患者に複数行(歯ごと)
HbA1c追跡シートHbA1c・体重等の時系列データ同一患者に複数行(測定日ごと)、動的列追加可

技術スタック

Python(PyMuPDF, OpenCV, Pillow)/ HTML+JS(GitHub Pages)/ Google Apps Script / Claude API / Gemini API / Google Spreadsheet + Drive

アンケート作成・開発

5ツール
アンケート作成ツール Vercel

概要

React+Viteで構築されたWebアプリ。医療機関を選択するとGoogleフォームのプレフィルURLが自動生成され、QRコードに変換してA4印刷用レイアウトに埋め込みます。

アルゴリズム

1. GAS API(getFormInfo)でフォームの公開URLとentry IDを取得
2. 医療機関名をentry IDに埋め込んだプレフィルURLを生成
3. URLをQRコードライブラリで変換
4. A4レイアウトにQRコード+アンケート内容を配置
→ アプリを開く
白紙アンケート Web

概要

医療機関名なしの汎用白紙アンケートPDFをダウンロードできるツールです。

→ アプリを開く
スキャンPDF分割 Web

概要

複数ページのスキャンPDFをドラッグ&ドロップで2ページずつに分割します。カスタムプレフィックスや連番設定が可能です。

→ アプリを開く
PDF結合(表裏統合) Web

概要

表面と裏面が別々にスキャンされたPDFファイルを、1つの2ページPDFに結合するツールです。

アルゴリズム

  • pdf-libでブラウザ上でPDFを結合(サーバー不要)
  • PDFDocument.load()で2つのPDFを読み込み → PDFDocument.create() → copyPages()で表裏を結合 → save()
  • 1組モード:表面・裏面を個別にドロップして結合
  • バッチモード:複数PDFをまとめて選択 → ファイル名順でペアリング(奇数番目=表面、偶数番目=裏面)→ 一括結合
  • Google Driveへ直接アップロード(GAS uploadSplitPdfアクション)またはローカルダウンロード
→ アプリを開く
PDFページ数チェック Web

概要

元PDFフォルダ(Google Drive)内の全PDFファイルのページ数を一括チェックし、2ページ(表裏セット)以外のファイルを検出・警告します。

アルゴリズム

  • GAS getDrivePdfListアクションで元PDFフォルダの全ファイルリストを取得
  • 各ファイルにGAS getPdfPageCountアクションを3件ずつ並列で呼び出し
  • ページ数 === 2 → ✅ 正常、それ以外 → ⚠️ 異常として判定
  • サマリーカード(合計/正常/異常/取得失敗)で結果を可視化
  • 異常ファイルを先頭にソートし、PDFビューアや結合ツールへのリンクを表示
  • フィルタ機能(全て/正常のみ/異常のみ)
→ アプリを開く
アンケートフォーム Google

概要

患者がQRコードからアクセスするGoogleフォーム。医療機関名が事前選択された状態で開きます。回答はスプレッドシートに自動保存されます。

touka-survey (GitHub) GitHub

概要

アンケート作成ツールのソースコードリポジトリ。React+Vite構成でVercelにデプロイされています。

📄

OCR読み取り・照合

13ツール
OCR読み取り+照合(メイン) ローカル

概要

メインのPython処理パイプライン(verify_survey.py、約3,300行)。スキャンPDFを画像化し、傾斜補正→基準点検出→質問領域切り出し→AI OCR→照合HTML生成を行います。

処理フロー

  • PDF選択 → PyMuPDFで画像変換(200-300 DPI)
  • 傾斜補正(2方式: テンプレートマッチング / Hough変換)
  • 基準点検出(2点参照方式)→ 相対座標で領域切り出し
  • Claude API / Gemini APIでOCR読み取り
  • 照合用HTML生成 → ブラウザで確認・編集
  • JSON保存 → PDF移動

傾斜補正アルゴリズム

方式1: テンプレートマッチング(デフォルト)
1. 二値化(Otsu法)→ 左マージン(幅の15%)で水平射影
2. テキスト行を検出し「質問」文字列のテンプレートを抽出
3. cv2.matchTemplate() で画像全体からマッチング(閾値0.7)
4. 2点(質問1+質問13[新]/質問15[旧])の座標で角度算出: atan2((x2-x1)/(y2-y1))
5. cv2.getRotationMatrix2D() で回転補正

方式2: Hough変換(フォールバック)
1. Canny エッジ検出 → HoughLinesP で直線検出
2. 近水平線(|角度| < 15°)をフィルタ → 中央値を傾き角度とする

2点参照方式

- 旧フォーマット(1ページ): 質問1 + 質問15 を基準点
- 新フォーマット(2ページ): 質問1 + 質問13 を基準点
- 相対座標(0.0〜1.0)で全質問の領域を定義(A4 300DPI基準: 2480×3509px)
- 用紙の位置ずれ・傾きに依存しない安定した切り出し

OCRモデル

- Claude API: claude-sonnet-4-20250514, temperature=0, max_tokens=4096
- Gemini API: gemini-3-flash-preview(フォールバック)
- 2段階方式: フルページ一括OCR → 個別フィールド再OCR(ID・生年月日等)
- 信頼度スコア(high/medium/low)+候補値の出力
Web OCR照合 Web

概要

Pythonなしでブラウザ上で完結するOCR照合ツール。Google DriveのPDFをPDF.jsで表示し、Gemini APIでOCR処理します。

左右パネル同期スクロール

1. PDF矩形の画面Y座標を getBoundingClientRect() で取得
2. 左パネル: 矩形をパネル上端に移動するスクロール量を算出(限界クランプ)
3. スクロール後のPDF矩形の予測画面Y座標を計算
4. 右パネル: 回答ブロックを予測Y座標に合わせるスクロール量を算出
5. 右パネルも限界到達 → 不足分だけ左パネルのスクロールを戻す
6. 両パネルを同時に scrollTo({ behavior: 'smooth' })

矩形データ管理

質問位置はマウスドラッグで定義→GAS ScriptPropertiesに永続保存。localStorageをキャッシュとして併用。

PDF表示

PDF.js canvas描画を基本とし、失敗時はBlob URL + embed方式にフォールバック(CSP制限対策)。

→ アプリを開く
全PDF_to_OCR Web

概要

Google Driveの元PDF(ScanData)フォルダ内の全PDFを一括でGemini OCR処理。進捗バー表示、個別再実行可能。結果はGAS ScriptPropertiesにJSON保存。

→ アプリを開く
OCR照合共有版 Web

概要

複数人が同時に照合作業できる共有版。排他ロック機構で衝突を防ぎます。

排他ロック機構

- ファイルのDescription(JSON)にロック状態を保存
- ステータス: unverifiedin_progressverified
- ロック取得時にlockedBy(ユーザー名)とlockedAt(タイムスタンプ)を記録
- 30分タイムアウトで自動解除(他のユーザーが取得可能に)
- Shift+OKで残り質問を一括確定
→ アプリを開く
PDF先読み照合 Web

概要

次のファイルをバックグラウンドで先読みし、待ち時間なく照合作業を進められるツール。排他ロック+Shift+OK一括確定に対応。

→ アプリを開く
紙回答自動OCR+照合 Web

概要

PDFアップロードまたはDriveインポート→Gemini AI自動OCR→結果編集→スプレッドシート直接送信。

→ アプリを開く
QRコード(紙回答) OCR→目視チェック Web

概要

事前OCR結果を自動ロード(待ち時間ゼロ)。PDFプレビューと並べて質問ごとに確認・編集し、スプレッドシートに保存。

→ アプリを開く
QRコード(フォーム回答)回答データ→PDF紐づけ Web

概要

Googleフォームの回答データとスキャンPDFを紐づけるツール。スコアベースマッチングで候補を自動提示します。

マッチングアルゴリズム

各フィールドにスコアを割り当て、複合スコアで候補を判定:
- ID番号: 0-30点(完全一致=30点、部分一致=Levenshtein類似度×30)
- 名前: 0-30点(Levenshtein距離による類似度)
- 生年月日: 0-20点(完全一致のみ)
- 医療機関名: 0-20点(完全一致=20点)
- 合計スコア70%以上で候補として提示

Levenshtein類似度 = (maxLen - distance) / maxLen

Q14抜歯位置入力

紐づけ時にQ14(抜歯位置×3行: 右/左+上/下+番号)を手入力。紐づけ完了後、PDFは入力済みフォルダへ自動移動。

→ アプリを開く
バッチOCR照合 Web

概要

LabelMe風のファイルリストUIで、複数のOCR結果を一括照合するツール。

→ アプリを開く
PDF処理状況チェック Web

概要

元PDF(ScanData)フォルダと入力済みPDFフォルダを比較し、未処理・処理済みファイル数を確認するツール。

→ アプリを開く
🗃

データ管理

8ツール
データ閲覧 Web

概要

パスワード保護された全回答データのテーブルビューア。No./ID/名前/医療機関名で検索・フィルタリング可能。重複No.をハイライト表示。

→ アプリを開く
PDFインデックス構築 Web

概要

Google DriveのPDFをGemini 3 Flash Previewで自動読み取りし、医療機関名・患者ID・名前・生年月日・QRチェック有無を抽出してインデックス化。

アルゴリズム

1. Google Driveの元PDF(ScanData)フォルダ内PDFを一覧取得
2. 各PDFをBase64エンコード → Gemini 3 Flash Preview APIに送信
3. OCRプロンプトで5フィールド(hospital, patientId, patientName, birthdate, qrCheckbox)を抽出
4. 結果をスプレッドシート「PDFインデックス」シートに保存
→ アプリを開く
検体番号検索 Web

概要

検体サンプルの氏名(カタカナ)や生年月日から、スプレッドシートの通し番号(No.)を検索するツール。

あいまい検索アルゴリズム

- カタカナ名前の部分一致(Levenshtein距離で類似度60%以上)
- 生年月日でも絞り込み可能
- 検索結果をLevenshtein距離昇順でソート
- No.クリックでクリップボードにコピー
→ アプリを開く
統合データエクスポート Web

概要

3つのスプレッドシートを患者No.で1行/患者に統合し、CSVダウンロードするツール。

統合アルゴリズム

GAS側(generateBigData関数):
1. メインシートを読み込み、No.をキーにpatientMapを構築
2. PpPシートを読み込み、各患者のPpPデータリストを収集 → maxPpp算出
3. 追跡シートを読み込み、各患者の追跡データリストを収集 → maxTrack算出
4. 統合ヘッダー = 基本列 + PpP1_〜PpPn_列 + 追跡1_〜追跡n_列
5. 各患者を1行に展開、No.昇順ソート

HTML側: 列グループ色分け(基本=白、PpP=水色、追跡=薄紫)、BOM付きUTF-8 CSV
→ アプリを開く
PDF到着状況 Web

概要

元PDF(ScanData)フォルダの到着PDFと入力済みPDFの比較。到着数・処理済数・未処理数を表示。

→ アプリを開く
テストデータ削除 Web

概要

開発・テスト時に登録したテストデータを、患者No.指定で削除するツール。パスワード保護。削除は取り消し不可。

→ アプリを開く
JSON復元 Web

概要

保存済みJSONファイルを読み込み、Googleスプレッドシートへデータを復元・再送信するツール。

→ アプリを開く

検査値入力

4ツール
追跡データ入力・閲覧・編集 Web

概要

HbA1c・体重・血糖値等の追跡データを入力・閲覧・編集するWebツール。動的に列(検査項目)を追加可能。

主な機能

  • 患者No.で検索→過去データ一覧表示
  • 動的列追加: 「列を追加」ボタンで新しい検査項目を追加
  • 編集モード: 既存データの上書き更新(GAS updateTracking)
  • URL入力対応: リンクとして表示
  • 縦型テーブル: 項目名を縦に並べて表示
→ アプリを開く
追跡データ(スプレッドシート直接) Sheets

概要

HbA1c追跡スプレッドシートを直接開くリンク。Googleスプレッドシートで直接データを確認・編集できます。

検体PpP値 入力・閲覧 Web

概要

検体のPEN・PYD値を入力し、PpP値を自動計算して記録するツール。

PpP値計算

PpP値 = PYD ÷ PEN(小数点以下4桁)

入力フロー:
1. 患者No.で検索 → 医療機関名・名前・生年月日を自動取得
2. 歯の位置(右/左+上/下+番号)を入力
3. PEN・PYD値を入力 → PpP値が自動計算
4. 登録ボタンで保存(検索済みでないと登録不可)

バリデーション

patientConfirmedフラグ: 検索ボタンを押して患者情報が確認されていないと登録できない仕組み。検索忘れによる空データ登録を防止。

→ アプリを開く
PpP値データ(スプレッドシート直接) Sheets

概要

検体PpP値スプレッドシートを直接開くリンク。

データフロー

3ツール
全体データフロー Web

概要

3プロジェクト間のデータの流れを図示。Group A(QRコード組)とGroup B(紙回答組)の分岐を可視化。

→ アプリを開く
作業フロー図 Web

概要

協力者の作業手順をフローチャートで表示。紙回答→フォーム入力→PDF紐づけ→抜歯位置入力→検体番号検索→PpP値入力の流れ。

→ アプリを開く
元PDF閲覧ビューア Web

概要

Google Driveの元PDF(ScanData)フォルダ内PDFを直接プレビューするビューア。ファイル名・日時でソート可能。

→ アプリを開く

GAS API リファレンス

Google Apps Script Web App(Code_gs_complete.js)はJSONP形式で通信し、actionパラメータでルーティングします。

主要アクション一覧

アクション機能認証
getHospitalList医療機関リスト取得なし
addHospital医療機関追加なし
addSurveyResponseアンケート回答追加(No.自動採番)なし
viewData全回答データ取得パスワード
getNoList患者No.一覧取得なし
addSpecimenPpP検体データ追加(10列)なし
getSpecimenListPpP検体データ取得なし
addTracking追跡データ追加なし
getTrackingList追跡データ取得なし
updateTracking追跡データ更新なし
addTrackingColumn追跡シートに列追加なし
generateBigData3シート統合データ生成パスワード
getDrivePdfListDrive PDFファイル一覧なし
indexPdfWithGeminiPDFをGemini OCRでインデックス化なし
lockOcrResult / unlockOcrResultOCR結果の排他ロックなし
askAssistantAIアシスタント(Gemini)パスワード