やったこと
前回(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の読み書き
次回やること
日付の入力で、デフォルト値を今日の日付にする。
コメント