体重を入力すると適正「身長」を教えてくれるアプリを作った

# あなたの身長、適正ですか？

皆さんはダイエットと聞くと何を思い浮かべるでしょうか。
糖質制限？有酸素運動？色々あると思います。
おそらく共通しているのは「体重を減らすこと」ではないでしょうか。しかし、それはダイエットの本質ではありません。

ダイエットの本質、それは、**体重と身長がバランスの取れた健康な体になること**です。
つまり、ダイエットのアプローチには大きく２つあります。![pptFA17.pptm [自動保存済み].png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135663/cef4c1f1-2e84-a1c8-dedb-b6113a1bd0dd.png)

今回私は「**身長を変える**」にフォーカスしたWEBアプリを作りました。

# 適正身長がすぐ分かるアプリ「せたけん」
こちらのリンクから無料で使うことができます。

https://share.streamlit.io/fujitako03/appropriateheight/main/app.py


ソースコードはGithubにアップロードされています

https://github.com/fujitako03/AppropriateHeight


# 使い方
## 直感的なUI
体重をスライド入力するだけで、適正身長がすぐに分かります！
![demo](https://gyazo.com/b4fcc79b080301e557ee69f098633e82/raw)

## オプション入力で専門家っぽい人からアドバイスもらえる
現在の身長を入力することで、専門家っぽい人からあなたにぴったりなアドバイスをもらえます！
![demo](https://gyazo.com/d964b6c6e57f7266afb19489c65bc0b0/raw)

## ヘルスケアアプリで唯一！？干支入力機能搭載
特に使い道はありませんが、干支を入力することができます！
![demo](https://gyazo.com/4926a58a63e9a467fd166da775898324/raw)

# 今後の展望
現在はリリース初期ということもあり、ターゲットを絞ったシンプルなサービスにとどめています。
今後はAIとかDXとかメタバースとか使って、サスティナブルな世界を目指していきたいと思います。

過去のクソアプリたち
- [おもしろいダジャレを入力すると布団が吹っ飛ぶ装置を作った](https://qiita.com/fujit33/items/dbfbd7a2aa3858067b6c)
- [【待ってました】ラーメンにコショウをかけるためのアプリを作りました！](https://qiita.com/fujit33/items/93d0b36f17816517b1b4)
- [ダーツが命中するかどうか、刺さる直前に教えてくれる装置を作った](https://qiita.com/fujit33/items/9ec26e3b35d65bcd494a)
- [RaspberryPiと異常検知を使って、ボケたらツッコんでくれるAIスピーカーを作った](https://qiita.com/fujit33/items/076eef5511a629352cca)

# 実装面の解説

## 概要
今回はなるべく手間をかけ無いことを優先し、[streamlit](https://streamlit.io/) を使いました。
streamlitとはhtmlやjavascriptなどをいっさい書かず、pythonだけでwebアプリを作れてしまうオープンソースのPythonライブラリです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135663/fb09a59d-3f53-ad57-59f3-e538933c5b7b.png)

さらにすごいのは、Streamlitのアカウントを作り、ソースコードGithubにpushしてリポジトリを連携することで、なんとWebアプリの公開まで自動でやってくれるのです。レンタルサーバ、gcp、aws、herokuを利用する必要はありません。
![demo](https://gyazo.com/9201de59daa1bf551f74f05deedd62b9/raw)

## コード解説
### Github
https://github.com/fujitako03/AppropriateHeight

### ディレクトリ構成
```
.
├── LICENSE
├── .streamlit
│   └── config.toml  -- 設定ファイル
├── app.py -- 実装スクリプト
├── eiyoshi.png -- 専門家っぽい人の画像
└── requirements.txt -- 必要なpythonライブラリ一覧
```

## 設定ファイル
リポジトリのトップに`.stremlit/config.toml`というファイルに設定を書くことで、自動的に読み込んでアプリに反映されます。
設定は以下のとおりです。

```toml
[theme]
primaryColor="#809C51" # 基本カラー（スライダーなどの色）
backgroundColor="#FFFFFF" # 背景色
secondaryBackgroundColor="#E4FFB7" #サイドバーなどの背景色
textColor="#262730" # 文字の色
```

## 実装スクリプト

### 基本の書き方
strealitはpythonスクリプトの中で定義したそのままの順で画面に表示されます。
解説も画面の上から順に説明していきます。

### スライダーで利用する関数

スライダーが更新されたときに、値をsession_stateに格納する関数を定義します。
値を表示する場所がスライダーよりも下の場合は必要ないのですが、今回は適正身長を上に大きく見せて、スライダーは下に置きたかったためこのような実装になっています。

```python
import math

import streamlit as st

def weight_slider_change():
    """体重スライダーが更新されるたびに体重と適正身長を更新
    """
    st.session_state.weight = st.session_state.weight_slider # スライダーから値を取得。スライダーのkeyと対応
    st.session_state.best_height = calc_appropriate_height(st.session_state.weight) # 適正身長を計算する関数

def height_slider_change():
    """身長スライダーが更新されるたびに現在身長を更新
    """
    st.session_state.now_height = st.session_state.height_slider  # スライダーから値を取得。スライダーのkeyと対応

```

### 初期化
体重や身長の値を初期化します。

```python
# 初期化
if 'weight' not in st.session_state:
    st.session_state.weight = 0

if 'best_height' not in st.session_state:
    st.session_state.best_height = 0

if 'now_height' not in st.session_state:
    st.session_state.now_height = None
```

### 適正身長の表示
stremlitの機能では中央寄せが無いため、マークダウンの中でhtmlを書くという荒業で対処しています。
また、フォントの色やサイズもここで調整しています。

```python
# 身長
st.markdown(
    f"<h1 style='text-align: center; color: #809C51;'> \
    あなたの適正身長は\
    </h1>", unsafe_allow_html=True)
st.markdown(
    f"<h1 style='text-align: center; color: #528540; font-size: 7em;'> \
    {st.session_state.best_height}\
    </h1>", unsafe_allow_html=True)
st.markdown(
    f"<h1 style='text-align: center; color: #809C51;'> \
    cm\
    </h1>", unsafe_allow_html=True)
```

### 体重スライダー
画面には表示されない部分ですが、ロジックを定義する関数を先にきさいしています。
スライダーは最小値最大値の設定のほか、スライダーのkeyやスライダーに変更を加えたときに動く関数を定義しています。

```python
# 体重
st.slider(
    '体重を入力してね', 
    min_value=0, 
    max_value=150,
    on_change=weight_slider_change,
    key="weight_slider")
```

### アドバイス
アドバイスは自分の身長が入力されたときだけ出したいので、now_heightに値が入っているかどうかで表示を制御しています。

```python
# アドバイス
if st.session_state.now_height:
    advice_text = get_advice(
        now_height=st.session_state.now_height,
        best_height=st.session_state.best_height)
    st.text(advice_text)
    st.image("./eiyoshi.png", width=100)
```

### オプション
オプションはst.expanderを使って、必要なとき以外デフォルトで閉じるようにしています。
性別と干支の入力にはラジオボタンを使っています。他にもたくさんのボタンがあるので、[公式ドキュメント](https://docs.streamlit.io/)を眺めてみると楽しいと思います。

``` python
# オプション
with st.expander("オプション"):
    st.slider(
        '現在の身長を入力してね', 
        min_value=50, 
        max_value=220,
        on_change=height_slider_change,
        key="height_slider")

    # 性別
    gender = st.radio("性別", ("未回答", "男性", "女性"))
    st.text("※性別は適正体重と無関係です")

    # 干支
    gender = st.radio("干支", ("未回答", "子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"))
    st.text("※干支は適正体重と無関係です")
```

### 作者と参考
マークダウンで記載しています。


```python
st.markdown("作者： [@fujitako](https://twitter.com/fujitako03)")
st.markdown("参考： [日本医師会](https://www.med.or.jp/forest/health/eat/11.html)")
```

# デプロイの方法
## アカウント作成
公式のサイトの「Get started」からポチポチ行けば作れます。私はComminuty Freeアカウントを作りました。GitHubアカウント認証で作成すると後々のGitHub連携も楽になると思います。
https://streamlit.io/cloud

## Githubとの連携
こちらも特に説明するほどのこともなく、ログインしたあとに
New appからリポジトリ名、ブランチ名、実行ファイル名を入力するだけです。

Advanced settingsからPythonのバージョン（3.6, 3.7, 3.8）や環境変数の入力ができます。

# 最後に
いかがだったでしょうか。非常にかんたんにWebアプリの作成から公開まで行うことができました。
見た目にこだわらず、小さなアプリであれば1時間程度で作成できてしまうのではないでしょうか。