４０代おっさん面会予約アプリを作ってみる④

## 本記事について

この記事はプログラミング初学者の私が学んでいく中でわからない単語や概要を分かりやすくまとめたものです。
もし不正などありましたらコメントにてお知らせいただければ幸いです。

## 前回の記事

<https://qiita.com/kou1121/items/bea7575176b07bd16bb9>
<https://qiita.com/kou1121/items/94ff8d65976dab98dfc8>
<https://qiita.com/kou1121/items/318fe6cc0e0564e533b4>

## ユーザー登録画面の作成

`app.py`を作る

```python
import streamlit as st
import random # ランダム値がほしいので
import datetime # 時刻を扱うため
import requests
import json

#  サイドバーを作って各ページを用意
page = st.sidebar.selectbox('ページを選択してください', ['ユーザー登録', '面会室登録', '面会予約'])

if page == 'ユーザー登録':

    st.title('登録画面')

    with st.form(key='user'):
        # user_id: int = random.randint(0, 10)
        username: str = st.text_input('ユーザー名', max_chars=12) # 12字しか入力できなくなる。
        data = {
            # 'user_id': user_id,
            'username': username
        }
        submit_button = st.form_submit_button(label='ユーザー登録') # form専用のボタン

    if submit_button: # ボタンが押されたされる処理
        st.write('## 送信データ') # デバック用
        st.write('## レスポンス結果')
        url = 'http://127.0.0.1:8000/users'
        res = requests.post(
            url,
            data=json.dumps(data)
        )
        if res.status_code == 200:
            st.success('ユーザー登録完了')
        st.write(res.status_code) # レスポンスのステータスコード
        st.json(res.json())
```

こちらで実行してみるとエラーが出てしまうので
usernameの値のみになってしまうので、前に作ったschemas.Userとデータの方が合わないので、そちらを直します。

`schemas.py`を直します

```python
import datetime
from pydantic import BaseModel, Field

class BookingCreate(BaseModel):
    user_id: int # ユーザーID usersテーブルと紐づけ
    room_id: int # 面会室ID roomsテーブルと紐づけ
    booked_num: int # 予約人数 面会室の定員まで
    start_datetime: datetime.datetime # 開始時刻
    end_datetime: datetime.datetime # 終了時刻

class Booking(BookingCreate):
    booking_id: int # 予約ID

    class Config:
        orm_mode = True # ormモデルに対応させる

class UserCreate(BaseModel):
    #  user_id: int は自動で生成されるのでいらない
    username: str = Field(max_length=12) # フォームから入力時に必要なため

class User(UserCreate): # UserCreateを継承 username: strいらない
    user_id: int # ユーザーID

    class Config:
        orm_mode = True # ormモデルに対応させる

class RoomCreate(BaseModel):
    room_name: str = Field(max_length=12) # 面会室名（12文字まで）
    capacity: int # 定員　各面会室ごとに定める

class Room(RoomCreate):
    room_id: int # 面会室ID

    class Config:
        orm_mode = True # ormモデルに対応させる

```

このように各クラスにCreate部分を作りました。
これはデータ登録時にいらないデータを省いてCreate側で受け取れるようにするためです。
次にこちらも変えます

`main.py`
のpost部分を変えます。

```python
# Create
@app.post("/users", response_model=schemas.User)
async def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
    return crud.create_user(db=db, user=user)

@app.post("/rooms", response_model=schemas.Room)
async def create_room(room: schemas.RoomCreate, db: Session = Depends(get_db)):
    return crud.create_room(db=db, room=room)

@app.post("/bookings", response_model=schemas.Booking)
async def create_booking(booking: schemas.BookingCreate, db: Session = Depends(get_db)):
    return crud.create_booking(db=db, booking=booking)
```

`schemas.UserCreate`の型を受け取れるようにしています。
ただし`return`しているのは`response_model=schemas.User`なのでUser型になっています。

## 面会室登録画面の作成

```python
import streamlit as st
import datetime # 時刻を扱うため
import requests
import json
import pandas as pd

#  サイドバーを作って各ページを用意
page = st.sidebar.selectbox('ページを選択してください', ['ユーザー登録', '面会室登録', '面会予約'])

if page == 'ユーザー登録':

  # 上を参照

elif page == '面会室登録':
    st.title('面会室登録画面')

    with st.form(key='room'):
        # room_id: int = random.randint(0, 10)
        room_name: str = st.text_input('面会室名', max_chars=12) # 12字しか入力できなくなる。
        capacity: int = st.number_input('定員', step=1) # 定員　１刻みで入力
        data = {
            # 'room_id': room_id,
            'room_name': room_name,
            'capacity': capacity
        }
        submit_button = st.form_submit_button(label='面会室登録') # form専用のボタン

    if submit_button: # ボタンが押されたされる処理
        st.write('## レスポンス結果')
        url = 'http://127.0.0.1:8000/rooms'
        res = requests.post(
            url,
            data=json.dumps(data)
        )
        if res.status_code == 200:
            st.success('面会室登録完了')
        st.write(res.status_code) # レスポンスのステータスコード
        st.json(res.json())

```

## 面会室の予約画面の作成

### ユーザー、面会室情報の取得

```python
# ユーザー一覧を取得
    url_users = 'http://127.0.0.1:8000/users'
    res = requests.get(url_users)
    users = res.json() # usersのリストが返ってくる
    # ユーザー名をキー、ユーザーIDをバリュー
    users_dict = {}
    for user in users:
        users_dict[user['username']] = user['user_id']

    # 面会室一覧の取得
    url_rooms = 'http://127.0.0.1:8000/rooms'
    res = requests.get(url_rooms)
    rooms = res.json() 
    rooms_dict = {}
    for room in rooms:
        rooms_dict[room['room_name']] = {
            'room_id': room['room_id'],
            'capacity': room['capacity']
        }
```

### 予約フォームの作成

```python
import streamlit as st
import datetime # 時刻を扱うため
import requests
import json
import pandas as pd

#  サイドバーを作って各ページを用意
page = st.sidebar.selectbox('ページを選択してください', ['ユーザー登録', '面会室登録', '面会予約'])

if page == 'ユーザー登録':

    # 上を参照

elif page == '面会室登録':
    
    # 上を参照

elif page == '面会予約':
    st.title('面会室予約画面')
    # ユーザー一覧を取得
    # 上を参照

    # 面会室一覧の取得
    # 上を参照

    st.warning('### 面会室一覧')
    df_rooms = pd.DataFrame(rooms)
    df_rooms.columns = ['面会室名', '定員', '会議室ID']
    st.table(df_rooms)
    
    with st.form(key='booking'):
        username: str = st.selectbox('予約者名', users_dict.keys()) # users_dict.keys()で名前を取ってこれる
        room_name: str = st.selectbox('面会室名', rooms_dict.keys()) # rooms_dict.keys()で面会室名を取ってこれる
        booked_num: int = st.number_input('予約人数', step=1, min_value=1) # 予約人数　１刻みで入力
        date = st.date_input('日付: ', min_value=datetime.date.today()) # 最小値（min）に今日（today）の日付を設定
        start_time = st.time_input('面会開始時刻: ', value=datetime.time(hour=9, minute=0))
        end_time = st.time_input('面会終了時刻: ', value=datetime.time(hour=20, minute=0))
        
        submit_button = st.form_submit_button(label='予約登録') # form専用のボタン

    if submit_button: # ボタンが押されたされる処理
        user_id: int = users_dict[username]
        room_id: int = rooms_dict[room_name]['room_id']
        capacity: int = rooms_dict[room_name]['capacity']

        data = {
            
            'user_id': user_id,
            'room_id': room_id,
            'booked_num': booked_num,
            'start_datetime': datetime.datetime( 
                year=date.year,
                month=date.month,
                day=date.day,
                hour=start_time.hour,
                minute=start_time.minute
            ).isoformat(), # str型でないとダメなため
            'end_datetime': datetime.datetime( 
                year=date.year,
                month=date.month,
                day=date.day,
                hour=end_time.hour,
                minute=end_time.minute
            ).isoformat()
            
        }
        # 定員以下の予約人数の場合
        if booked_num <= capacity:
            # 面会室予約
            url = 'http://127.0.0.1:8000/bookings'
            res = requests.post(
                url,
                data=json.dumps(data)
            )
            if res.status_code == 200:
                st.success('予約完了しました')
            st.json(res.json())

        else:
            st.error(f'{room_name}の定員は、{capacity}名です。')
```

## 参考

https://www.udemy.com/course/python-fastapi