仮説検定を行うwebアプリケーションの作成

#クライエントサイド

```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" enctype="multipart/form-data">
    <label>タイトル</label>
    <input class="form-control" type="text" size="30" name="title">
    <input type="file" id="send_data" name="send_data">
    <input type="submit" value="送信">
</form>

{% endblock %}
```

```index.htlm
{% 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 %}
 
```

```veiw.html
{% extends "base.html" %}
{% block body %}
<h1>結果参照</h1>
 
<h3>{{ result.id }}:{{ result.title|safe}}</h3>
<h4>{{result.test}}</h4>
<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 %}
```
#サーバーサイド

```models.py
from matplotlib import pyplot as plt
from pandas.plotting import scatter_matrix
import pandas as pd
import time
import io
import scipy.stats as stats

def create_scatter(df_csv):
    #フォームから入力されたデータをcsvにしている
    #data = data.replace(',', '\t').replace(' ', '\t')
    #df = pd.read_csv(io.StringIO(data), sep='\t')
 
 
    # プロットマーカー
    # の大きさ、色、透明度を変更
    scatter_matrix(df_csv, 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 test(df_csv):
    
    #正規性の検定（ks検定）
    ks_1 = stats.kstest(df_csv[df_csv.columns[0]], "norm")
    ks_2 = stats.kstest(df_csv[df_csv.columns[1]], "norm")
    if ks_1.pvalue < 0.05 and ks_2.pvalue < 0.05:
        t_w = stats.ttest_ind(df_csv[df_csv.columns[0]], df_csv[df_csv.columns[1]])
        if t_w.pvalue > 0.05:
            p = '''帰無仮説は棄却されない
            ウェルチのt検定 p値:'''+str(t_w.pvalue)
            return p
        else:
            p ='''帰無仮説は棄却される
            ウェルチのｔ検定　p値:'''+str(t_w.pvalue)
            return p
    else :
        #ウィルコクソンの順位和検定
        p='ウィルコクソンの順位和検定　面倒だから計算しません'
        return p



def select_all(con):
    """ SELECTする """
    cur = con.execute('select id, title, data, img, test, created from results order by id desc')
    return cur.fetchall()


def select(con, pk):
    """ 指定したキーのデータをSELECTする """
    cur = con.execute('select id, title, data, img, test, created from results where id=?', (pk,))
    return cur.fetchone()


def insert(con, title, data, img, test):
    """ INSERT処理 """
    #cursorオブジェクトのおかげでデータベースに対して様々な処理を実行できるありがとう
    cur = con.cursor()
    cur.execute('insert into results (title, data, img, test) values (?, ?, ?, ?)', [title, data, img, test])
    #多分最後に追加された行のROWIDを取得している
    
    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()
```

```run.py
import os
from flask import Flask, redirect, url_for, render_template
from flask import Flask, redirect, url_for, render_template, request, g
import sqlite3
import models
from flask import Flask, request, g, redirect, url_for, render_template, flash
import csv
from werkzeug.utils import secure_filename
import pandas as pd
import io
app = Flask(__name__)
#app.config.updataでflaskの設定が出来る
app.config.update(dict(
    DATABASE=os.path.join(app.root_path, 'db.sqlite3'),
    SECRET_KEY='foo-baa',
))
app.config.from_object(__name__)

ALLOWED_EXTENSIONS = set(['csv'])
app.config['SECRET_KEY'] = os.urandom(24)
UPLOAD_FOLDER = './uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER



# 以下、DB接続関連の関数

 
def connect_db():
    """ データベス接続に接続します """
    con = sqlite3.connect(app.config['DATABASE'])
    con.row_factory = sqlite3.Row
    return con
 
 
def get_db():
    """ connectionを取得します """
    #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')

def allowed_file(filename):
    return '.' in filename and \
        filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/analysis', methods=['GET', 'POST'])
def analysis():
    """ 分析実行処理 """
    #リクエストがホストかどうかの判別
    if request.method == 'POST':
        #ファイルがなかった時の処理
        if 'send_data' not in request.files:
            flash('ファイルがありません')
            return redirect(request.url)
        #データの取り出し
        send_data = request.files['send_data']
        #入力されたファイル名がなかった時の処理
        if send_data.filename == '':
            flash('ファイルがありません')
            return redirect(request.url)
        #ファイルのチェック
        if send_data and allowed_file(send_data.filename):
            #危険な文字を削除（サニタイズ処理)
            filename = secure_filename(send_data.filename)
            #ファイルの保存
            print(send_data,type(send_data))
            send_data.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            df_csv = pd.read_csv('uploads/' +filename)

            title = request.form['title']
            #stringio()は文字列をファイルとして扱うことが出来る
            buffer = io.StringIO()
            df_csv.to_csv(buffer, index=False)    # indexを指定しないと行番号が出力される
            data = buffer.getvalue()
            img = models.create_scatter(df_csv)
            test = models.test(df_csv)
            con = get_db()
            #データベースに入れている
            pk = models.insert(con, title, data, img, test)
            flash('登録処理が完了しました。') 
            #pkにROWIDを入れてなにしとるん？pk=pkは何してる？
            return redirect(url_for('view', pk=pk))
    """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)
    print(result["img"])
    return render_template('view.html', result=result)


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

```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,
  `test` text,
  `created` datetime default CURRENT_TIMESTAMP
);```