出席管理システム — 教育現場の要件にアーキテクチャで応える
背景と課題
開業時から技術面で参画している個別指導塾「Onedrop」では、不登校経験のある生徒を含む多様な子どもたちが通っています。開業時から出席管理システムを導入しており、QRコードによる自動記録と月次レポートの自動生成を核として、段階的に機能を拡張してきました。
設計判断: 「記録」ではなく「自己理解」のための質問
本システムの特徴は、入室時にQRコードをスキャンした後、3つの質問に回答する仕組みを設けている点です。
| 質問 | 内容 | 選択肢の例 |
|---|---|---|
| 気分 | 今日の気分はどうですか? | 天気アイコンで直感的に選択 |
| 睡眠 | 昨日はよく眠れましたか? | 3段階 |
| 来所目的 | 今日は何をしに来ましたか? | 勉強、友達と遊ぶ、相談、等 |
この3問は単なるデータ収集ではありません。特別支援教育の研究で学んだ知見を設計に反映しています。不登校経験のある子どもは、自分の状態を言葉にすることが難しい場合があります。毎日同じ質問に答える習慣を通じて、自分のコンディションを客観視する力を育てることを意図しています。
質問のUIにはKivyのタッチインターフェースを採用し、テキスト入力を排除しました。天気アイコンをタップするだけで回答が完了するため、言語化が苦手な子どもでも抵抗なく使えます。
システム構成
| コンポーネント | 技術 | 役割 |
|---|---|---|
| デスクトップアプリ | Python + Kivy | QRコード読み取り、入退室記録、質問UI |
| Webアプリ | Flask + Bootstrap | 月次レポートの編集・プレビュー |
| データ管理 | Google Sheets API (gspread) | リアルタイムデータ同期 |
| レポート生成 | ReportLab / openpyxl | PDF統計レポート・Excelレポート出力 |
| ラベル印刷 | Brother P-touch連携 | 名札・QRコードの直接出力 |
技術選定の根拠
Kivy を選んだのは、Windowsデスクトップアプリとしてタッチ操作に対応でき、Pythonエコシステムの資産(ReportLab、openpyxl、Google API クライアント)をそのまま活用できるためです。Electronも検討しましたが、既存のPythonスクリプト群との統合コストを考慮してKivyを選定しました。
ただし、Kivyには「1プロセスにつき1アプリしか起動できない」という制約があります。本システムでは生徒向けの入退室画面と管理者ダッシュボードの2画面が必要だったため、multiprocessingで2つのKivyアプリを並行起動し、フラグファイル方式で起動シーケンスを同期する仕組みを実装しました。生徒向けウィンドウはWin32 APIでセカンダリディスプレイに自動配置されます。
Google Sheets をデータストアとして採用したのは、講師がスプレッドシート上で直接データを確認・編集できるためです。既存の業務フローとの親和性を重視した判断です。
Flask によるWebアプリは、月次レポートの編集画面として後から追加しました。当初はデスクトップアプリ内でレポート編集も行っていましたが、講師がブラウザからレポートを確認・編集したいという運用ニーズに対応するために分離しています。
UDデジタル教科書体 を全コンポーネント(Kivy UI、PDF、Excel)で統一採用しています。教育施設向けシステムとして、可読性の高いユニバーサルデザインフォントを選定しました。
段階的な進化
v3.2: Google連携版
初期バージョンでは、Google Sheets APIを中心としたアーキテクチャを採用しました。入退室データはリアルタイムでスプレッドシートに同期され、Google Apps Scriptによるメール通知も実装しています。
この段階で発生した技術的な問題の一つが、GASトリガーの誤動作です。onChangeトリガーがAPI経由の書き込みでは期待通りに発火しないケースがあり、タイムスタンプベースの判定ロジックに修正しました。この問題はドキュメント化し、同様の構成を取る場合の参考として残しています。
v3.4: 機能拡張と耐障害性
運用を続ける中で、2つの課題が見えてきました。
1. ネットワーク障害への対応
教室のWi-Fi環境は安定しないことがあり、Google Sheets APIへの通信が失敗するケースが発生しました。入退室の記録という業務上クリティカルな処理が通信障害で停止することは許容できません。
対策として、指数バックオフ付きリトライを追加しました。ここで重要なのは、リトライがすべて失敗した場合の設計判断です。安易にデフォルト値を返すと、前回の入退室記録が取得できないまま処理が進み、誤った入室登録が発生する可能性があります。そのため、リトライ全失敗時はエラーとして処理を中断し、「入退室の処理は行われていません」とユーザーに明示する設計にしています。業務上クリティカルなデータの整合性を、利便性より優先した判断です。
2. 2ウィンドウ構成への移行
小学生と中学生以上で異なるスプレッドシートを使い分ける運用に対応するため、年齢グループ別の2ウィンドウ構成を実装しました。生徒パターン(D/R)に基づいて自動分類し、それぞれ独立した記録先に同期します。
リファクタリング: モノリスからモジュール分離へ
機能追加に伴いコードベースが肥大化したため、構造の整理を行いました。
- 画面ごとにファイルを分割(
screens/ディレクトリへの移動) - UI共通コンポーネントの導入によるダイアログ表示の統一
- リソース管理の一元化によるコード重複の削減
- 設定ファイルの拡張によるマジックナンバーの排除
この整理により、新しい画面や機能を追加する際の影響範囲が明確になり、保守性が向上しました。
レポート生成の設計
月次レポートの自動生成は、運用上最も時間を削減した機能です。
PDF生成(ReportLab) では、UDデジタル教科書体のTTCフォント埋め込み、カテゴリ別色分けの棒グラフ、カレンダー風の出席表示を実装しています。生徒ごとの出席統計、質問回答の傾向、講師コメント欄を1枚のレポートにまとめています。
Excel出力(openpyxl) は、保護者への配布資料として使用しています。手動のExcelテンプレートと同一レイアウトをプログラムで再現し、プランニング・カウンセリング・個別対応の列にはプルダウンバリデーションを設定しています。
ラベル印刷(Brother P-touch) では、P-touch Editorのコマンドライン引数を使ったCSVデータベース連携で名札を自動印刷します。実装上の課題として、P-touch EditorのCSVエンコーディング互換性の問題がありました。Shift-JIS、CP932、UTF-8 BOM、UTF-8の4種類のエンコーディングを順に試行するフォールバック処理で対応しています。ハードウェア連携特有の泥臭い問題ですが、現場で確実に動作することを優先しました。
これにより、毎月の報告書作成にかかる時間を数時間から数分に短縮しています。
運用から得た知見
外部依存の最小化
Google Sheets APIへの依存は、開発初期には合理的な選択でした。しかし運用を続ける中で、ネットワーク障害時のリスクが顕在化しました。この経験から、コア機能は外部サービスに依存しない設計にすべきだという知見を得ました。
この経験から、以降のプロジェクトでは「ローカルファースト」の設計思想を採用しています。コア機能をローカルで完結させ、外部連携は同期レイヤーとして分離する方針です。
ユーザーフィードバックの重要性
開発者の想定と実際の運用環境は異なります。Web版レポートエディタの追加も、2ウィンドウ構成への移行も、すべて運用中のフィードバックから生まれた改善です。最初から完璧な設計を目指すのではなく、実際に使いながら段階的に改善するアプローチが、教育現場のような変化の多い環境では有効でした。
技術スタック
| 技術 | 用途 |
|---|---|
| Python 3.x | アプリケーション全体 |
| Kivy | デスクトップUI(タッチ対応) |
| Flask + Bootstrap | Web版レポートエディタ |
| Google Sheets API | データ同期 |
| Google Apps Script | メール通知 |
| ReportLab | PDF生成(日本語フォント対応) |
| openpyxl | Excel出力 |
| Brother P-touch | ラベル印刷連携 |