よちよちpython

独習 python/Qpython/Pydroid3/termux/Linux

【Flask Pandas】売上データからドロップダウンメニューで担当者を指定して抽出、ページに表示する

今回は、PandasのデータフレームをWebページに表示させます。
売上データから担当者を指定して表示させるアプリ。



実行環境

  • Windows10
  • Anaconda 4.11.0
  • Python3.9.7
  • VSCode 1.63.2
  • 外部ライブラリ
    • Flask 2.0.2
    • colorama 0.4.4 (Windowsはたぶん手動でインストールが必要
    • Pandas 1.3.5



アプリの仕様・操作手順など

df
No 売上日 担当者 商品 単価 数量
0 1 2022-01-09 立花宗茂 牛乳 139 9
1 2 2022-01-08 島津義弘 イチゴ 416 6
2 3 2022-01-05 島津義弘 牛乳 281 3
3 4 2022-01-03 上杉謙信 牛乳 151 6
4 5 2022-01-06 豊臣秀吉 牛乳 474 1
... ... ... ... ... ... ...
995 996 2022-01-01 上杉謙信 バター 150 10
996 997 2022-01-02 上杉謙信 107 10
997 998 2022-01-10 織田信長 砂糖 462 8
998 999 2022-01-10 北条氏康 砂糖 416 10
999 1000 2022-01-05 毛利元就 225 10

1000 rows × 6 columns

上のような売上データがあるとします。(顧客名を入れ忘れた
担当者を指定して、その担当者の売上テーブルを表示するアプリを作ります。

データフレームから条件抽出するにはdf.query()df[df['カラム名']==値]などを使えばできます。

df[df['担当者']=='織田信長']
No 売上日 担当者 商品 単価 数量
12 13 2022-01-03 織田信長 384 1
42 43 2022-01-09 織田信長 イチゴ 144 9
55 56 2022-01-10 織田信長 砂糖 455 3
72 73 2022-01-07 織田信長 小麦粉 138 4
73 74 2022-01-07 織田信長 バター 335 6
... ... ... ... ... ... ...
970 971 2022-01-03 織田信長 バター 488 1
973 974 2022-01-10 織田信長 バター 437 2
981 982 2022-01-04 織田信長 478 5
983 984 2022-01-01 織田信長 チョコレート 297 2
997 998 2022-01-10 織田信長 砂糖 462 8

108 rows × 6 columns

メニューから担当者を選択して、↑のような表をWebページに表示するのが今回の目標です。



データフレームに.to_html()メソッドを付けると(df.to_html())、テーブルがhtmlコードに変換されます。そのオブジェクトをFlaskのテンプレートに渡せばテーブルを表示してくれるはずです。

  • 担当者のユニークなリストを取得するには
df['担当者'].unique().tolist()
['立花宗茂', '島津義弘', '上杉謙信', '豊臣秀吉', '北条氏康', '毛利元就', '武田信玄', '織田信長', '徳川家康']

このリストをホームページに設置するドロップダウンメニューのselectタグに渡して、テンプレートエンジンの記法のfor文で回せばメニューが出来る。



アプリの操作手順

  1. ホームページに設置したドロップダウンメニューで担当者を選択し、送信する。
  2. 送信された担当者の売上データをデータフレームから抽出。
  3. テーブルで表示



20220117015645

ホームページ。ドロップダウンメニューで担当者を選択し、送信。



20220117015810

指定した担当者の売上データが表示される。



20220117015858

CSSでスタイル変更してみた。



ディレクトリ構成とファイルの中身

アプリはショボいんですが…

$ tree flask_sample

flask_sample/
├── app.py (Flaskを動かす)
├── static
│   └── css
│       └── style.css (表の装飾。試しに付けた)
├── templates
│   ├── base.html (htmlページのメタ情報テンプレート)
│   ├── index.html (ホームページ。入力欄とボタンを設置)
│   └── result.html (表の出力ページ)
└── uriage_data.py (データフレームの生成用)

3 directories, 6 files



app.py (Flaskを動かす用)

from flask import Flask, render_template, request
from uriage_data import uriage

app = Flask(__name__)

# 売上データ
df, tantou_list = uriage()

# ホームページ
@app.route('/')
def index():
    return render_template('index.html', tantou_list=tantou_list)

# 入力値の表示ページ
@app.route('/result', methods=['GET', 'POST'])
def result():
    # 担当者名を取得
    tantou_name = request.form.get('tantousya')
    # データフレームから指定担当者の売上を抽出
    df_tantou = df[df['担当者']==tantou_name]
    # 指定担当者のデータフレームをhtmlに変換
    df_tantou = df_tantou.to_html()

    if request.method == 'POST':
        return render_template('result.html', tantou_name = tantou_name, dataframe = df_tantou)
    else:
        return render_template('index.html')

if __name__=='__main__':
    app.run(debug=True)

uriage_data.py (データフレーム生成用)

app.pyの中に書いてもよかったのですが、読みにくいかなと思って分けました。

import pandas as pd
import random
random.seed(0)

# 売上データを生成
def uriage():
    df = pd.DataFrame({
        'No' : range(1, 1001),
        '売上日' : random.choices(pd.date_range(start='2022/1/1', freq='d', periods=10), k=1000),
        '担当者' : random.choices('織田信長 豊臣秀吉 徳川家康 毛利元就 北条氏康 武田信玄 上杉謙信 島津義弘 立花宗茂'.split(), k=1000),
        '商品' : random.choices('卵 牛乳 砂糖 小麦粉 バター チョコレート チーズ イチゴ'.split(), k=1000),
        '単価' : random.choices(range(100, 501), k=1000),
        '数量' : random.choices(range(1,11), k=1000)
    })

    # 担当者をリストで取得
    tantou_list = df['担当者'].unique().tolist()

    return df, tantou_list

base.html (htmlファイルのメタ情報)

htmlファイルのだいたい共通している部分なので別に分ける。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="static/css/style.css">

    <!-- タイトル -->
    <title>Flaskサンプル-{% block title %}{% endblock %}</title>  
</head>

<!-- コンテンツ内容 -->
<body>
    {% block content %}
    {% endblock %}
</body>

</html>

外部CSSファイルを用意したので、<link rel="stylesheet" href="static/css/style.css">を付けました。「style.css」というファイルを「static/css/」に保存しているので。
これを書くだけで自動的にCSSファイルを読み込んでくれます。

index.html (ホームページ)

入力欄とボタンを設置

{% extends 'base.html' %}

{% block title %}ホームページ{% endblock %}

{% block content %}
<form action="/result" method="POST">
  <p>担当者:
    <select name="tantousya">
    {% for i in tantou_list %}
    <option value="{{ i }}">{{ i }}</option>
    {% endfor %}
    </select>
  </p>
  <p><input type="submit" value="送信"></p>
</form>
{% endblock %}

selectタグoptionタグでドロップダウンメニューを実装できます。 app.pyファイルから渡されたtantou_listをfor文で回してメニューを作っています。
tantou_listuriage_data.pyで売上データフレームから取り出した担当者リスト。
送信ボタンを押すと、selectタグname属性'tantousya'はFlask側app.pyに送信されます。

result.html (指定担当者の表出力ページ)

{% extends 'base.html' %}

<!-- タイトル表示 -->
{% block title %}{{ tantou_name }}さんの売上データ{% endblock %}

<!-- コンテンツ内容の表示 -->
{% block content %}

{{ dataframe | safe }}

{% endblock %}

htmlテンプレートファイルにhtmlコードを渡すと、自動でエスケープされてページにコードが文字列で表示されてしまいます。
{{ dataframe | safe }}と書いていますが、パイプ|で繋いでsafeとすることでエスケープが外れてhtmlコードが実行表示できるようになります。

style.css (指定担当者売上テーブルの装飾用)

html/css未学習のため、cssの書き方が分からず、適当にネットからコピペ・改造したものです。外部CSSの効かせ方を把握する目的。

.dataframe{
    /*テーブルの縦幅が100px以上の場合スクロール*/
    height: 100px;
    /*縦スクロール*/
    overflow: scroll;
}
 
/* 一覧表の調整 */
td {
    border-bottom: 1px solid black;
    border-left: 1px solid black;
    border-right: 1px solid black;
    text-align: center;       
}
th {
    background-color: #ddffeb;
    border-bottom: solid black;
    border-left: solid black;
    border-right: solid black;
    border-top: solid black;
    border-width: 2px;
    position: sticky;
    top: 0;
    text-align: center; 
}
/* カラムの調整 */
.column {
    overflow: hidden;/*スクロール時にカラム名を固定*/
    text-overflow: ellipsis;
}

テーブルが縦長になったのでスクロールバーをつけようとしたが、よく分からない。とりあえずCSSは効いてるようだ。



実行

ターミナルでFlaskを実行し、サーバが起動したらブラウザでローカルホストの5000番にアクセス。

PS > python app.py

ブラウザで127.0.0.1:5000localhost:5000にアクセス。
ホームページが表示される。あとは上の「アプリの操作手順」通り。



おわりに

通常、データはデータベースに登録し、表示する際にSQLなどで取り出すと思います。今回のようにデータフレームをテーブルにして表示することはしないとは思いますが、どうなるかの確認でした。
ショボいアプリなのにファイルが6個もある。変数値がファイルをまたいでアッチコッチに飛ぶので、設計書か何かで動作の全体像を把握しないと混乱する。Webやってる人スゲェ。



以上です。