こんにちは、ナナオです。

前回の記事で図書館の予約管理システムを構築しました。

データ取得まではできましたが、データ出力するところが実装できなかったので今回実装していこうと思います。

React使うか~とかも思ったのですが、Geminiと相談した結果Streamlitというライブラリを使うことにしました。

Streamlitとは

グラフデータや表データなどを含むWebアプリを超簡単に構築できるライブラリです。

Streamlit • A faster way to build and share data apps

もちろん自由度はReactでフロントエンドを作るよりも低くはなるのですが、大体の機能が揃っているのでとりあえずデータ出力したいだけであればこれで十分だと思います。

実装

ということで実装していきます。

今回は以下のデータクラスを表で出力します。

@dataclass
class LoanStatusItem:
    deadline_at: dt.date
    borrower_name: str
    library_name: str
    book_title: str

@dataclass
class ReserveItem:
    status: Literal["reserved", "already"]
    borrower_name: str
    library_name: str
    book_title: str

必要なライブラリを追加します。

uv add streamlit pandas

実装はとりあえずダミーデータを使ってやりました。

def get_dummy_data():
    # 貸出状況のデータ
    loans = [
        LoanStatusItem(dt.date(2023, 10, 15), "佐藤", "中央図書館", "Python入門"),
        LoanStatusItem(dt.date(2023, 10, 20), "鈴木", "北図書館", "Streamlit実践"),
        LoanStatusItem(dt.date(2023, 10, 12), "佐藤", "中央図書館", "機械学習の基礎"),
    ]
    
    # 予約状況のデータ
    reserves = [
        ReserveItem("reserved", "佐藤", "南図書館", "Web設計パターン"),
        ReserveItem("already", "田中", "中央図書館", "デザイン思考"),
        ReserveItem("reserved", "鈴木", "北図書館", "Docker活用"),
    ]
    
    current_time = dt.datetime.now().strftime('%H:%M:%S')

    return loans, reserves, current_time

def main():
    st.title("図書館 利用状況ダッシュボード")
    
    # --- 更新ボタンの配置 ---
    # col1, col2 を使うことで、タイトルやボタンの配置を調整できます
    col1, col2 = st.columns([3, 1])
    
    with col1:
        st.write("最新の貸出・予約状況を表示します。")
    
    with col2:
        # ボタンが押されたらキャッシュをクリアして再実行
        if st.button("最新情報を取得 🔄"):
            st.cache_data.clear()  # キャッシュを削除
            st.rerun()             # スクリプトを再実行(画面更新)

    # データの取得
    loans, reserves, fetched_time = get_dummy_data()
    
    # 更新時刻の表示
    st.caption(f"データ取得時刻: {fetched_time}")

    # --- 1. 貸出状況の表示 ---
    st.subheader("📅 貸出状況 (Loan Status)")
    if loans:
        # dataclassのリストをDataFrameに変換
        # dataclasses.asdictを使うと辞書に変換され、DataFrame化しやすくなります
        df_loans = pd.DataFrame([asdict(item) for item in loans])
        
        # カラム名の見た目を整える(任意)
        df_loans = df_loans.rename(columns={
            "deadline_at": "返却期限",
            "borrower_name": "利用者名",
            "library_name": "図書館",
            "book_title": "書名"
        })
        
        # テーブル表示 (use_container_width=Trueで横幅いっぱいに表示)
        st.dataframe(df_loans, use_container_width=True)
    else:
        st.info("貸出中の本はありません。")

    st.markdown("---") # 区切り線

    # --- 2. 予約状況の表示 ---
    st.subheader("🔖 予約状況 (Reserve Status)")
    if reserves:
        df_reserves = pd.DataFrame([asdict(item) for item in reserves])
        
        # カラム名の整理
        df_reserves = df_reserves.rename(columns={
            "status": "状態",
            "borrower_name": "利用者名",
            "library_name": "図書館",
            "book_title": "書名"
        })

        # 状態(reserved/already)に応じて色をつけたりも可能です
        st.dataframe(df_reserves, use_container_width=True)
    else:
        st.info("予約中の本はありません。")

if __name__ == "__main__":
    main()

これを実行します。

実行はstreamlit cliを使用します。

streamlit run [スクリプトのパス]

実行結果はこんな感じです。

表のソートなども可能です。

すげーなSteamlit。

感想

恐ろしく簡単にWebUIが実装できました。

簡単なデータ参照とかに使うWebUIならこれでいいですね。