bottleでPandasのDataFrameをCSV形式でダウンロードする。

やったこと

前回(bottleでテンプレートを継承してみる。)からの続き。
CSVファイルをダウンロードするところでいろいろつまづいた(bottleでCSVファイルをダウンロードさせるときにつまづいたところ)が、なんとか解決できた。

1.データを集計してDataFrameの形にしたものを、CSV形式でダウンロードする。
つまづいたところ」に記載しているように、文字コードで苦しんだが、
一旦to_csv()でform.pyが存在しているディレクトリにCSVファイルをSHIFT-JISで書き出し、
書き出したCSVファイルをダウンロードするようにプログラムを組んだ。

2.入力フォームに「日付」を入力する欄を追加した。
<input type = “date” />を使うと非常に便利。ブラウザ上でカレンダーが表示される。

作成コード

ディレクトリ構成

.
├── form.py
└── views
    └── base.html
    └── form.html
    └── form_failed.html

 

form.pyに、to_csv()でDataFrameをCSVファイルで書き出すコードを追記。
HTTPResponseに書き出したCSVファイルをダウンロードするための情報を記載。

日付データをフォームの入力項目から取得し、データをエクスポートする関数に入れる。

# coding:utf-8
import bottle#,jinja2
from bottle import route, run, template, get, post, request, response, HTTPResponse
from bottle import TEMPLATE_PATH, jinja2_template as template
import requests
from requests.auth import HTTPBasicAuth
import json
from json import loads
from datetime import datetime as dt
import datetime
import numpy as np
import pandas as pd
import csv

TEMPLATE_PATH.append("./views")

    (中略)

# Togglのデータをエクスポート
def data_export(email,password,date):

    (中略)

    #Pandasでデータを集計
    pivot1 = df.pivot_table(index = ['Folder','Description'], values = 'Duration', aggfunc = 'sum', fill_value = 0)
    pivot2 = pd.to_datetime(pivot1['Duration']).map(lambda x: '{:%H:%M:%S}'.format(x))

    #データフレームに変換
    pivot3 = pivot2.to_frame()

    # ファイル名を指定
    filename = 'Toggl_export_' + date.replace("-","") + '.csv'
    # filename = 'Toggl_export_' + today.strftime('%Y%m%d') + '.csv'

    # CSVファイルを書き出す。
    pivot3.to_csv('Toggl_export.csv',encoding = "SHIFT-JIS")

    # 書き出したCSVファイルをダウンロード
    with open('./Toggl_export.csv','rb') as f:
        content = f.read()
    response = HTTPResponse(body = content)
    response.content_type = "text/csv"
    # ダウンロードするファイル名を指定
    response.headers["Content-Disposition"] = "attachment; filename=" + filename
    return response

    (中略)

@route('/download',method = ["GET", "POST"]) # or @route('/', method='POST')
def download():
    """
    POSTで/にアクセスした際の処理
    """
    # フォームからPOSTされたデータを取得する
    email = request.forms.get('email')
    password = request.forms.get('password')
    date = request.forms.get('date')
    # ログイン判定を行う
    if check_status_code(email, password,date):
        title = "認証成功"
        return data_export(email,password,date)
        # template("form_success.html", title = title)
    else:
        title = "認証失敗"
        return  template('form_failed.html', title = title)

(参考ページ)
Python + Bottle でファイルのダウンロードを実装
pandasでcsvファイルの書き出し・追記(to_csv)

form.htmlに、日付を入力する欄を追加。

{% extends 'base.html' %}

{# base.html の contents の中に入れるコンテンツ #}
{% block contents %}

<h1>Togglのデータをエクスポートするツールです。</h1>
<form action = "/download" method = "post">
メールアドレス: <input name = "email" type = "email" /><br>
パスワード  : <input name = "password" type = "password" /><br>
日付     :<input name = "date" type = "date" /><br>
<input value="ダウンロード" type="submit" />
</form>

{% endblock %}

(参考ページ)
input type=”date”を使った日付入力フォーム

実行結果

ブラウザで開くと、日付を入力する欄が追加されている。
日付のところは、プルダウンを開くとカレンダーが表示される。

ダウンロードしたファイルは、文字コードがSHIFT-JISで、文字化けもしていない。

つまづいたところ

header情報を記載するところで、DataFrameをto_csv()でCSV形式に書き出すようにしておくと、
to_csv(encoding = “SHIFT-JIS”)で文字コードを指定しても、ダウンロードしたファイルの文字コードがUTF-8(Python3系のデフォルト文字コード)
でダウンロードされる。その結果、ダウンロードしたファイルが文字化けしている(以下の画像)。
コードは以下。

# coding:utf-8
import bottle#,jinja2
from bottle import route, run, template, get, post, request, response, HTTPResponse
from bottle import TEMPLATE_PATH, jinja2_template as template
import requests
from requests.auth import HTTPBasicAuth
import json
from json import loads
from datetime import datetime as dt
import datetime
import numpy as np
import pandas as pd
import csv
# from io import BytesIO as IO
from io import StringIO# as IO

TEMPLATE_PATH.append("./views")

        (中略)

@route('/download',method = ["GET", "POST"]) # or @route('/', method='POST')
def download():

        (中略)

        #Pandasでデータを集計
        pivot1 = df.pivot_table(index = ['Folder','Description'], values = 'Duration', aggfunc = 'sum', fill_value = 0)
        pivot2 = pd.to_datetime(pivot1['Duration']).map(lambda x: '{:%H:%M:%S}'.format(x))

        #データフレームに変換
        pivot3 = pivot2.to_frame()
        # pivot3 = pivot2.as_matrix()

        # ファイル名を指定
        filename = 'Toggl_export_' + date.replace("-","") + '.csv'

        # CSVファイルのダウンロード用にheaderに情報を記載
        response = HTTPResponse(body = pivot3.to_csv(encoding = "SHIFT-JIS"))#.decode('utf-8').encode('shift_jis'))
        response.headers["Content-Type"] = "text/csv"#; charset = SHIFT_JIS"
        response.headers["Content-Disposition"] = "attachment; filename = " + filename
        return response

 

参考記事

参考までに、PandasのDataFrameは、以下のようなwriter.writerowメソッドは使えない。

import csv

with open('some.csv', 'w') as f:
    writer = csv.writer(f, lineterminator='\n') # 改行コード(\n)を指定しておく
    writer.writerow(list)     # list(1次元配列)の場合
    writer.writerows(array2d) # 2次元配列も書き込める

(参考ページ)
PythonでCSVの読み書き

次回やること

日付の入力で、デフォルト値を今日の日付にする。

コメント

タイトルとURLをコピーしました