補足として完成後の構成とソースコードを掲載します。
Contents
プロジェクト
構成は以下のとおりとなります。
.
├── db.sqlite3
├── models.py
├── requirements.txt
├── run.py
├── schema.sql
├── static
│ ├── result
│ └── style.css
└── templates
├── base.html
├── edit.html
├── index.html
└── view.html
schema.sql
drop table if exists results; create table results ( `id` integer primary key autoincrement, `title` text not null, `data` text not null, `img` text not null, `created` datetime default CURRENT_TIMESTAMP );
requirements.txt
利用するライブラリは以下のとおりです。(2018/4/21 修正 scipyを追記しました。)
click==6.7 cycler==0.10.0 Flask==0.12.2 itsdangerous==0.24 Jinja2==2.10 kiwisolver==1.0.1 MarkupSafe==1.0 matplotlib==2.2.0 numpy==1.14.1 pandas==0.22.0 pyparsing==2.2.0 python-dateutil==2.6.1 pytz==2018.3 scipy==1.0.1 six==1.11.0 Werkzeug==0.14.1
Pythonコード
run.py
import os
import sqlite3
from flask import Flask, request, g, redirect, url_for, render_template, flash
import models
app = Flask(__name__)
app.config.from_object(__name__)
app.config.update(dict(
DATABASE=os.path.join(app.root_path, 'db.sqlite3'),
SECRET_KEY='foo-baa',
))
def connect_db():
""" データベス接続に接続します """
con = sqlite3.connect(app.config['DATABASE'])
con.row_factory = sqlite3.Row
return con
def get_db():
""" connectionを取得します """
if not hasattr(g, 'sqlite_db'):
g.sqlite_db = connect_db()
return g.sqlite_db
@app.teardown_appcontext
def close_db(error):
""" db接続をcloseします """
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()
# 以下、画面に関わるメソッド
@app.route('/')
def index():
""" 一覧画面 """
con = get_db()
results = models.select_all(con)
return render_template('index.html', results=results)
@app.route('/create')
def create():
""" 新規作成画面 """
return render_template('edit.html')
@app.route('/analysis', methods=['POST'])
def analysis():
""" 分析実行処理 """
title = request.form['title']
data = request.form['data']
img = models.create_scatter(data)
con = get_db()
pk = models.insert(con, title, data, img)
flash('登録処理が完了しました。')
return redirect(url_for('view', pk=pk))
@app.route('/delete/<pk>', methods=['POST'])
def delete(pk):
""" 結果削除処理 """
con = get_db()
models.delete(con, pk)
flash('削除処理が完了しました。')
return redirect(url_for('index'))
@app.route('/view/<pk>')
def view(pk):
""" 結果参照処理 """
con = get_db()
result = models.select(con, pk)
return render_template('view.html', result=result)
if __name__ == '__main__':
app.run()
models.py
"""
ビジネスロジックモジュール
"""
from matplotlib import pyplot as plt
from pandas.plotting import scatter_matrix
import pandas as pd
import time
import io
def create_scatter(data):
data = data.replace(',', '\t').replace(' ', '\t')
df = pd.read_csv(io.StringIO(data), sep='\t')
# プロットマーカーの大きさ、色、透明度を変更
scatter_matrix(df, diagonal='kde', color='#AAAAFF', edgecolors='#0000FF', alpha=0.5)
# ファイル名
filename = time.strftime('%Y%m%d%H%M%S') + ".png"
# 保存先のパス
save_path = "./static/result/" + filename
# 表示用URL
url = "result/" + filename
# 保存処理を行う
plt.savefig(save_path)
# pltをclose
plt.close()
return url
def select_all(con):
""" SELECTする """
cur = con.execute('select id, title, data, img, created from results order by id desc')
return cur.fetchall()
def select(con, pk):
""" 指定したキーのデータをSELECTする """
cur = con.execute('select id, title, data, img, created from results where id=?', (pk,))
return cur.fetchone()
def insert(con, title, data, img):
""" INSERTする """
cur = con.cursor()
cur.execute('insert into results (title, data, img) values (?, ?, ?)', [title, data, img])
pk = cur.lastrowid
con.commit()
return pk
def delete(con, pk):
""" 指定したキーのデータをDELETEする """
cur = con.cursor()
cur.execute('delete from results where id=?', (pk,))
con.commit()
テンプレート
base.html
<!doctype html>
<title>簡易分析ツール</title>
<head>
<title>簡易分析ツール</title>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<link rel="stylesheet" type=text/css href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<header>
<div class="navbar navbar-dark bg-dark box-shadow">
<div class="container d-flex justify-content-between">
<a href="/" class="navbar-brand d-flex align-items-center">
<strong>簡易分析ツール</strong>
</a>
</div>
</div>
</header>
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-success">{{ message }}</div>
{% endfor %} {% block body %}{% endblock %}
</div>
</body>
</html>
edit.html
{% extends "base.html" %}
{% block body %}
<h1>新規分析</h1>
<form action="{{ url_for('analysis') }}" method="post">
<label>タイトル</label>
<input class="form-control" type="text" size="30" name="title">
<label>分析データ</label>
<textarea class="form-control" name="data" rows="5"></textarea>
<input class="btn btn-primary" type="submit" value="送信">
</form>
{% endblock %}
index.html
{% extends "base.html" %}
{% block body %}
<h1>分析一覧</h1>
<a href="/create">新規分析</a>
<table class="table table-striped table-hover">
<tr>
<th>id</th>
<th>title</th>
<th>date</th>
<th>操作</th>
</tr>
{% for result in results %}
<tr>
<td>{{ result.id }}</td>
<td>{{ result.title|safe}}</td>
<td>{{ result.created }}</td>
<td>
<a href="/view/{{ result.id }}"><button class="btn btn-primary">参照</button></a>
<form action="/delete/{{ result.id }}" style="display: inline" method="post">
<input class="btn btn-danger" type="submit" value="削除" onclick='return confirm("削除しますがよろしいですか?")';>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
2018/4/27 index.htmlのdelete部分に誤記があったため修正しました。(ご指摘くださった方ありがとうございます。)
view.html
{% extends "base.html" %}
{% block body %}
<h1>結果参照</h1>
<h3>{{ result.id }}:{{ result.title|safe}}</h3>
<p>{{ result.created }}</p>
<div class="row">
<img src="{{ url_for('static', filename=result.img) }}">
</div>
<div class="row">
<textarea class="form-control" name="data" rows="5">{{result.data}}</textarea>
</div>
<br><br>
{% endblock %}
