Metabase APIで変数付きのリクエストを投げる

# はじめに

下図のように、Metabaseで変数を設定したSQLクエリに対して、PythonからAPIを叩いて、クエリ結果を取得します。

![metabase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329888/74924fc9-6137-dc2a-e7aa-9eb2887d3f1e.png)

# 環境

```bash

$sw_vers
ProductName:    Mac OS X
ProductVersion: 10.13.6
BuildVersion:   17G14042
```

Metabase への接続については[metabase_api](https://github.com/vvaezian/metabase_api_python)を使用しました。
```pip install metabase-api```でインストール済みです。

Metabaseの構築については[こちら](https://qiita.com/dyamaguc/items/1cba8cd11ac40408522c)を参照。

# Pythonコード

Metabaseへのリクエスト送信用のコードが下になります。

```MetabaseConnector.py
from metabase_api import Metabase_API
from enum import Enum
from typing import List, Union
import pandas as pd
import json
import os

class MbVariableType(Enum):
    NUMBER = 'number'
    DATE = 'date/single'
    TEXT = 'category'


class MaVariable:
    def __init__(self, var_name: str, var_type: MbVariableType, var_value: Union[str, int, float]):
        self.var_name: str = var_name
        self.var_type: MbVariableType = var_type
        self.var_value: Union[str, int, float] = var_value


class MetabaseConnector:

    def __init__(self):
        self.mbcon = Metabase_API(
                domain=os.getenv("METABASE_DOMAIN"),
                email=os.getenv("METABASE_EMAIL"),
                password=os.getenv("METABASE_PASSWORD")
            )

    def getQuestionToDf(self, id: int, params: List[MaVariable]=None ) -> pd.DataFrame:
        url = '/api/card/' + str(id) + '/query/json'
        
        if params is not None:
            parameters = []
            for iparam in params:
                parameters_ = {
                        'type': iparam.var_type.value,
                        'target':['variable', ['template-tag', iparam.var_name]],
                        'value': iparam.var_value
                        }
                parameters.append( json.dumps(parameters_, ensure_ascii=False) )
            
            url += '?parameters=['+','.join(parameters)+']'
        
        response = self.mbcon.post(url)

        return pd.DataFrame(data=response)
```

上記のコードでは、Metabaseへリクエストを投げた結果を`pandas`の`DataFrame`に変換したかったため、JSONフォーマットで出力するAPIを叩いています。
APIについては[公式ドキュメント](https://www.metabase.com/docs/latest/api-documentation.html)を参照してください。

実行用のコードが下です。

```run.py
from MetabaseConnector import MbVariableType, MaVariable, MetabaseConnector

params = [
    MaVariable("from", MbVariableType.DATE, '2021-03-10'),
    MaVariable("minNumLgtm", MbVariableType.NUMBER, 3),
    MaVariable("keyword", MbVariableType.TEXT, "%変換%")
]

df = MetabaseConnector().getQuestionToDf(id=11, params=params)

print(df.head())
print(len(df))

```

本記事の一番上に添付した画面キャプチャに示している変数の設定と同じにしています。
実行すると192行分のデータが取得できるはず。

環境変数にMetabaseのURL、ユーザ名、パスワードを設定しておきます。

```bash
export METABASE_DOMAIN="http://192.168.0.20" # MetabaseのURL
export METABASE_EMAIL="user@sample.com" # Metabaseのユーザ名
export METABASE_PASSWORD="PASSWORD" # Metabaseのユーザパスワード
```

実行します。

```bash

$python run.py 
   numPV             title
0  15488  Box-Cox変換を理解してみる
1  15490  Box-Cox変換を理解してみる
2  15490  Box-Cox変換を理解してみる
3  15490  Box-Cox変換を理解してみる
4  15490  Box-Cox変換を理解してみる
192
```

Metabase上で実行した結果と同じ結果を得ました。

# 参考にした記事

- https://www.metabase.com/docs/latest/api-documentation.html
- https://qiita.com/kurousa/items/f59ea18db9e33ef446f0
- https://qiita.com/nkriskeeic/items/73e86db7ec4658d2669b