Web-WF Python Tornado その３（Openpyexcelの紹介）


# はじめに
webServer APIServerとして、かんたん・すぐれている（と思っている）Tornadoを何回かにわけて紹介したいと思います

[Web-WF Python Tornado その１]
(https://qiita.com/sin_hayasi/private/c5af3b21e7abda196cfd)
[Web-WF Python Tornado その２]
(https://qiita.com/sin_hayasi/private/bdc4f45b092ed5433aec)
Web-WF Python Tornado その３ （この記事

## 対象
フルスタックエンジニア、フロントエンジニア、Pythonが好きな人、その１・その２を読んだ人、Excelでなにか資料を出力する必要に迫られた人

Python3.6以上、tornadoがインストールされている

## ゴール
今回はちょっと趣向をかえてアプリケーションにあると便利なモジュールの紹介
（Openpyxl）

# Openpyxlとは

pythonからExcelファイルを読み書き編集できるモジュール
集計結果をExcelでレポート出力したり、逆にExcelのデータをインポートするのに便利です
今回は請求書を出力するサンプルを作成します
[公式はこちら]
(https://openpyxl.readthedocs.io/en/stable/)

### インストール
Openpyxlのインストールはpipのみ

```
$ pip install openpyxl
```

### excelテンプレートの準備

以下のようなテンプレートを準備しておきます
（赤字の部分はマクロのサンプル）
![スクリーンショット 2020-08-24 18.43.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672217/97e45878-116b-37bb-a8e0-721efe136111.png)


### ソースコード

その１やその２で紹介したソースに以下のソースを追加しておきます

```python:main.py
import os
import datetime
import openpyxl as px
from openpyxl.writer.excel import save_virtual_workbook

class prtInvoice(tornado.web.RequestHandler):
    def get(self):
        # 請求書のtemplateをLoad
        wb = px.load_workbook(BASE_DIR+u'/template.xlsx')
        ws = wb.active
        # 印刷範囲を指定
        ws.page_setup.fitToWidth = 1
        ws.page_setup.fitToHeight = 0
        ws.sheet_properties.pageSetUpPr.fitToPage = True
        # こんなmodelを準備（実際にはDBなどから
        model = {
            'companyName': 'テスト会社',
            'items': [
                {'itemName': '商品A', 'price': 1000, 'quantity': 1},
                {'itemName': '商品B', 'price': 2000, 'quantity': 2},
                {'itemName': '商品C', 'price': 3000, 'quantity': 3}
            ]
        }

        # 今日の日付をセット
        # F2はセルのExcelのセル
        dt_now = datetime.datetime.now()
        ws['F2'] = dt_now.strftime("%Y 年 %m 月 〆 日")
        # 会社名をセット
        ws['B7'] = '{0}　御中'.format(model['companyName'])
        for i, item in enumerate(model['items']):
            # 商品名をセット
            ws['B'+str(i+25)] = '　　{0}'.format(item['itemName'])
            # 数量をセット
            ws['D'+str(i+25)] = item['quantity']
            # 単価をセット
            ws['E'+str(i+25)] = item['price']

        # Excel形式でダウンロード
        self.set_header('Cache-Control',
                        'no-store, no-cache, must-revalidate, max-age=0')
        self.set_header(
            'Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        fileName = "Invoice.xlsx"
        self.set_header('Content-Disposition',
                        "attachment; filename=\"{}\"".format(fileName))
        self.write(save_virtual_workbook(wb))

def make_app():
    return tornado.web.Application(
        [
        (r"/html", htmlHandler),
        (r"/json", jsonHandler),
        (r"/excel", prtInvoice),
        ],
        debug=True,
    )
```

```
ws['B7'] = 'Hello'
```
こんなふうに、Excelのセルにデータをセットしたり、読み込んだりできるのがこのモジュールの特徴となります

### 実行＆閲覧

以下のようにURLを直接指定することでExcelファイルをダウンロードすることができます
（クライアントのダウンロードフォルダに直接保存される）

![スクリーンショット 2020-08-24 18.54.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672217/53def11f-5744-a025-6f16-8838cb4ed251.png)

ダウンロードしたExcelファイルを開くと
![スクリーンショット 2020-08-24 18.56.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672217/728bda70-278b-2b97-4998-e7f78fdc5eb3.png)
はい、なんとかそれっぽいデータが表示できました