やりたいこと
- 入退管理システムの開発
- フォームに会員IDを入力
- データベースで照合
- 会員情報を記録
導入
Ubuntu-22.04
$ git colone https://github.com/masaki130/GymSystem.git
$ cd gym_system
$ python app2.py
フォルダ構成
Ubuntu-22.04
├── gym_system
├── __pycache__
│ └── db.cpython-310.pyc
├── app2.py # Flaskサーバ
├── db.py # DB接続関数の定義
├── gym.db # DB本体
├── init_db.py # DB作成
├── schema.sql # DBテーブル作成
├── seed.py # DBに情報を登録
└── templates
└── index2.html # UI
テーブルの作成
schema.sql
-- SQLiteに3つのテーブルを作成
-- ==========================
-- 会員テーブル
-- ==========================
CREATE TABLE IF NOT EXISTS members ( -- 無かったら作る
member_id TEXT PRIMARY KEY, -- 文字列型, 主キー(一意のID)
name TEXT NOT NULL, -- NOT NULL; 必須
email TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP -- 現在時刻
);
-- ==========================
-- 入退室履歴
-- ==========================
CREATE TABLE IF NOT EXISTS entry_log (
log_id INTEGER PRIMARY KEY AUTOINCREMENT, -- 自動で増える履歴番号
member_id TEXT NOT NULL,
action TEXT NOT NULL CHECK(action IN ('IN','OUT')), -- 中と外を判別
access_time DATETIME DEFAULT CURRENT_TIMESTAMP, -- 入退室した時間
FOREIGN KEY(member_id) -- 外部キー, membersテーブルに存在するmember_idしか登録できない
REFERENCES members(member_id)
);
-- ==========================
-- 現在の状態
-- ==========================
CREATE TABLE IF NOT EXISTS current_status ( -- 現在館内にいるか
member_id TEXT PRIMARY KEY,
is_inside INTEGER NOT NULL DEFAULT 0, -- 初期値として0
last_updated DATETIME, -- 現在の時刻
FOREIGN KEY(member_id)
REFERENCES members(member_id)
);
DBに接続するための関数
db.py
# gym.dbに接続する処理
import sqlite3
import os
DATABASE = "gym.db"
# 絶対パス
print(os.path.abspath(DATABASE))
# データベースに接続する処理
def get_connection():
conn = sqlite3.connect(DATABASE)
conn.row_factory = sqlite3.Row # SQLiteから取得したデータを,列名でアクセスできるようにする設定
return conn
DBの作成
init_db.py
# schema.sqlに書かれたSQLを実行して,gym.dbを初期化
# db.pyで作ったget_connection()関数を読み込む
from db import get_connection
def init_database():
# gym.dbに接続(db.pyに定義済み)
conn = get_connection()
# schema.sqlを開く
with open("schema.sql", "r", encoding="utf-8") as f:
# 読み込んだ複数のSQLを一度に実行
conn.executescript(f.read())
conn.commit() # gym.dbに変更を保存
conn.close() # gym.dbの接続を終了
print("データベースを作成しました。")
# 直接実行した時のみ動く
if __name__ == "__main__":
init_database()
DBに会員情報を登録
seed.py
# gym.dbに,テスト用の初期データを登録する処理
# db.pyで定義したget_connectionを読み込む
from db import get_connection
# 初期データを登録する処理
def seed_members():
conn = get_connection() # gym.dbに接続
cursor = conn.cursor() # pythonとsqlの仲介役
# 会員データ
members = [
("1001", "山田 太郎", "yamada@example.com"),
("1002", "佐藤 花子", "sato@example.com"),
("1003", "田中 次郎", "tanaka@example.com")
]
# executemany;各会員分,同じSQLを複数回実行
# ?;プレースホルダー, 取得した値が入る
cursor.executemany("""
INSERT OR IGNORE INTO members
(member_id,name,email)
VALUES (?,?,?)
""", members)
# 現在の状態(全員外にいる)
current = [
("1001", 0),
("1002", 0),
("1003", 0)
]
cursor.executemany("""
INSERT OR IGNORE INTO current_status
(member_id,is_inside)
VALUES (?,?)
""", current)
conn.commit() # gym.dbに変更を保存
conn.close() # gym.dbの接続を終了
print("初期データを登録しました。")
if __name__ == "__main__":
seed_members()
UIの作成
index2.html
<!DOCTYPE html>
<html>
<head>
<title>入退室管理システム</title>
</head>
<body>
<h1>入退室管理システム</h1>
<!--波カッコはJinja2の記法-->
<h2>現在利用人数:{{ count }}人</h2>
<hr>
<!--入力されたデータをapp2.pyの/scanへPOST送信-->
<form action="/scan" method="POST">
<label>会員ID:</label>
<!--入力フォーム-->
<input
type="text"
name="member_id"
placeholder="例:1001"
required>
<!--ボタンを押すとフォームを送信-->
<button type="submit">
確定
</button>
</form>
<hr>
<h2>会員一覧</h2>
<table border="1"> <!--表を作成-->
<tr>
<th>ID</th> <!--表の列名-->
<th>名前</th>
</tr>
<!--Jinja2のループ文-->
{% for member in members %}
<tr>
<td>{{ member["member_id"] }}</td> <!--表にデータを表示-->
<td>{{ member["name"] }}</td>
</tr>
{% endfor %}
</table>
<hr>
<h2>最新の入退室履歴</h2>
<table border="1">
<tr>
<th>日時</th>
<th>名前</th>
<th>状態</th>
</tr>
{% for log in logs %}
<tr>
<td>{{ log["entry_time"] }}</td>
<td>{{ log["name"] }}</td>
<td>{{ log["action"] }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
アプリ本体
app2.py
# 管理システムのメイン処理
from flask import Flask, render_template, request, redirect, url_for
from db import get_connection
from datetime import datetime
# Webサーバを作成
app = Flask(__name__)
# http://127.0.0.1:5000/に接続後, index()を実行
@app.route("/")
def index():
conn = get_connection()
cursor = conn.cursor() # cursor; pythonからsqlを操作可能
# 現在人数をカウント
cursor.execute("""
SELECT COUNT(*)
FROM current_status
WHERE is_inside=1
""")
# 取得したsqlの結果からカウント数だけを取り出す
count = cursor.fetchone()[0]
# 会員一覧
cursor.execute("""
SELECT *
FROM members
ORDER BY member_id
""")
members = cursor.fetchall()
# 最新履歴(JOIN; 表を結合, ORDER BY entry_time DESC; 新しい順, LIMIT; 20件に制限)
cursor.execute("""
SELECT
entry_log.entry_time,
members.name,
entry_log.action
FROM entry_log
JOIN members
ON entry_log.member_id=members.member_id
ORDER BY entry_time DESC
LIMIT 20
""")
logs = cursor.fetchall()
conn.close()
# htmlの表示(index2.htmlに値を渡す)
return render_template(
"index2.html",
count=count,
members=members,
logs=logs
)
# フォーム受信した時に実行
@app.route("/scan", methods=["POST"])
def scan():
# フォームから送られたデータを取得
member_id = request.form["member_id"] # member_id; index2.html内で付けたid, 入力された会員番号が入る
conn = get_connection()
cursor = conn.cursor()
# 会員確認(WHERE; 条件文)
cursor.execute("""
SELECT *
FROM members
WHERE member_id=?
""", (member_id,))
# データベースから1行を取得
member = cursor.fetchone()
# 空の時
if member is None:
conn.close()
return "会員が存在しません"
# 現在の状態を取得
cursor.execute("""
SELECT is_inside
FROM current_status
WHERE member_id=?
""", (member_id,))
# データベースから1行を取得
status = cursor.fetchone()
# 入退判定
if status["is_inside"] == 0:
action = "IN"
inside = 1
else:
action = "OUT"
inside = 0
# 履歴追加(INSERT INTO; テーブルにデータを追加)
cursor.execute("""
INSERT INTO entry_log
(member_id, action, entry_time)
VALUES (?,?,?)
""", (member_id, action, datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
# 状態更新(改行してるだけで1つのSQL文)
cursor.execute("""
UPDATE current_status
SET
is_inside=?,
last_updated=?
WHERE member_id=?
""", (
inside,
datetime.now(),
member_id
))
conn.commit()
conn.close()
# 最初の画面に戻る
return redirect(url_for("index"))
if __name__ == "__main__":
app.run(debug=True)
結果
今後の予定
- QRコードやカードリーダーを使用した入退管理システムの開発
- UIをカッコよくする
- AIを搭載する
