Tableau Server Client（TSC）を使ってTableauOnlineからPDFレポートを出力

# やりたいこと
1. TableauOnline上にワークブックがある（都道府県別の売上ダッシュボード）
2. ワークブックには複数のビュー（タブ・ダッシュボード）がある
3. その複数ビューをPDF1ファイルで出力したい
4. ワークブックにはフィルターがあり、値を変えながらPDF出力したい（都道府県のフィルターがあり、47都道府県分のPDFレポートを作りたい）
　

1,2,3までならば、TableauOnlineに標準で用意されているPDF出力機能をボタン操作すればよい。4の要件が追加されたとたん、GUI操作を47回もする羽目になるので自動化したい。

# TableauServerClient(TSC)とは

TableauServerにあるデータをPythonで操作するライブラリ。
TableauServer RESTAPIで実装されていることは大体できる。

▼公式ドキュメントはこちら
https://tableau.github.io/server-client-python/docs/

# やってみた

## 1都道府県分を出力
### TSCの初期設定

```py
import tableauserverclient as TSC

# サイトIDはTableau OnlineのURL中の/site/に続く文字列
tableau_auth = TSC.TableauAuth('user_id', 'user_password', site_id='サイトID')

# xxxxの部分はポッドIDを入力。ポッドIDは利用中のTableau OnlineのURLに含まれているので確認すること
server = TSC.Server('https://xxxx.online.tableau.com/')

# APIバージョンは以下のURL内の対応表から取得し、指定する
# https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_versions.htm
server.version = '3.15'
```
### ワークブックIDの検索

今回は特定のワークブックをPDF出力したい。
ワークブックのIDはTableauOnline上では確認できなかったので、上の初期設定に続いてTSCの検索機能で調べる。
```py
with server.auth.sign_in(tableau_auth):
    all_workbooks_items = server.workbooks.filter(name='ワークブックの名前')
    
    for workbook in all_workbooks_items:
        print(workbook.id)
        print(workbook.name)
```
```'ワークブックの名前'```の部分に調べたいワークブックの名称（TableauOnline上で命名している名前）を打って、ワークブックのIDを探す。

### 対象ワークブックをPDF出力

PDF出力に必要な情報を入力

```py
#ワークブックのIDを指定
workbook_id = 'ワークブックのID'


#PDF出力時の用紙サイズ等の設定（下記ドキュメントにその他の指定方法が載ってます）
#https://tableau.github.io/server-client-python/docs/api-ref#pdfrequestoptions-class
pdf_req_option = TSC.PDFRequestOptions(page_type=TSC.PDFRequestOptions.PageType.A4,orientation=TSC.PDFRequestOptions.Orientation.Landscape,maxage=100)

#オプション設定（フィルターの値の変更）
pdf_req_option.vf("Select_area","東京都")
```

```pdf_req_option.vf("Select_area","東京都")``` がミソ。これでダッシュボード内にある「Select_area」というフィルターの値を「東京都」に指定した状態。後述のPDF出力操作時にこの```pdf_req_option```のパラメータを渡すと、「東京都」のPDFが出力される（これを47都道府県分for文回せばよい。


```py
import PyPDF2
import os

#PDF出力
with server.auth.sign_in(tableau_auth):

    #ワークブックを取得
    workbook_item = server.workbooks.get_by_id(workbook_id)
    
    #ワークブック内にある各ビューのビューIDを取得
    server.workbooks.populate_views(workbook_item)
    
    #---以下、ビューにPDFを出力して、それを最後にマージして1つのPDFデータにする---
    #temp_pdf用の通し番号
    page_num = 1 
    #pdfをマージするためのリスト
    pdf_list = PyPDF2.PdfFileMerger() 
    #temp pdfを削除するとき用のリスト
    tmp_list = [] 
    
    for view_item in workbook_item.views:
                
        #各ビューのpdfのオブジェクトを作成
        server.views.populate_pdf(view_item,pdf_req_option)

        #各ビューのpdf（temp_pdf)を出力
        with open('C:\\Users\\…\\tmp'+ str(page_num) + '.pdf', 'wb') as f:
            f.write(view_item.pdf)

        #各ビューのpdf（temp_pdf)をリスト化
        pdf_list.append(temp_pdf)
        tmp_list.append(temp_pdf)
        
        page_num = page_num + 1 

    #各ビューのPDFをマージ
    pdf_list.write('C:\\Users\\…\\Downloads\\' + filter_values + '.pdf') 
    pdf_list.close()
        
    #不要となった各ビューのPDF（temp_pdf)を削除
    for tmp_pdf in tmp_list:
        os.remove(temp_pdf)
       
```

## まとめ
今まで書いたプログラムをまとめる。今回やりたいことは次のプログラムで実現できる。
```py
import tableauserverclient as TSC
import PyPDF2
import os


#PDF出力先フォルダを指定
output_dir = 'C:\\Users\\…\\Downloads\\'

# サイトIDはTableau OnlineのURL中の/site/に続く文字列
tableau_auth = TSC.TableauAuth('user_id', 'user_password', site_id='サイトID')

# xxxxの部分はポッドIDを入力。ポッドIDは利用中のTableau OnlineのURLに含まれているので確認すること
server = TSC.Server('https://xxxx.online.tableau.com/')

# APIバージョンは以下のURL内の対応表から取得し、指定する
# https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_concepts_versions.htm
server.version = '3.15'

#出力条件の指定
workbook_id = 'ワークブックのID'
filter_values = ['東京都','京都府',…]

#PDF出力時の用紙サイズ等の設定
pdf_req_option = TSC.PDFRequestOptions(page_type=TSC.PDFRequestOptions.PageType.A4,orientation=TSC.PDFRequestOptions.Orientation.Landscape,maxage=100)

#サーバ接続＆PDF出力・加工
with server.auth.sign_in(tableau_auth):

    #ワークブックIDを指定
    workbook_item = server.workbooks.get_by_id(workbook_luid)
    #ワークブック内のビューを取得
    server.workbooks.populate_views(workbook_item)
    
    #フィルターごと（都道府県ごと）にループ
    for filter_value in filter_values:
    
        #フィルター値の変更
        pdf_req_option.vf("Select_area",filter_values)
    
        #temp_pdf用の通し番号
        page_num = 1 
        #pdfをマージするためのリスト
        pdf_list = PyPDF2.PdfFileMerger() 
        #temp pdfを削除するとき用のリスト
        tmp_list = [] 
    
        #ビューごとにループ（各ページのtempPDFを出力）
        for view_item in workbook_item.views:
                    
            #各ビューのpdfのオブジェクトを作成
            server.views.populate_pdf(view_item,pdf_req_option)

            #各ビューのpdf（temp_pdf)を出力
            with open(output_dir + 'tmp'+ str(page_num) + '.pdf', 'wb') as f:
            f.write(view_item.pdf)
            
            #各ビューのpdf（temp_pdf)をリスト化
            pdf_list.append(temp_pdf)
            tmp_list.append(temp_pdf)
            
            page_num = page_num + 1 

        #各ビューをマージ（1つのPDFに統合）
        pdf_list.write(output_dir + filter_values + '.pdf') 
        pdf_list.close()
        
        #不要となった各ビューのPDF（temp_pdf)を削除
        for tmp_pdf in tmp_list:
            os.remove(temp_pdf)



```

# 参考
https://www.ai-shift.co.jp/techblog/1577