Pythonを使ってドルを円に変換するコマンドラインツールを作る


# 概要
1. [為替確認API](http://api.aoikujira.com/kawase/)からレートのリストを取得する
2. コマンドライン引数で受け取った値をドルから円に変換する

```bash
$ brew tap kei-sato/usdex
$ brew install usdex
$ usdex -p 2.5 -v
1(USD) => 122.54078(JPY)
2.5(USD) => 306.35195(JPY)
```

Gistはこちらです
https://gist.github.com/kei-sato/98675769952ec7538d6a

brew install出来るようにする方法はこちらです
[Pythonで作ったコマンドラインツールをbrew install出来るようにする](http://qiita.com/kei-sato/items/276afa8dd8197fc91772)

# 解説
現在の為替情報をJSONを始めとした様々なフォーマットで公開（[http://api.aoikujira.com/kawase/](http://api.aoikujira.com/kawase/))してくださっている方がいらっしゃったので、これを利用して、コマンドラインからドルを円に換算するコマンドを作成しました。
まずは、APIから返ってくる値を確認します。

```bash
$ curl -s http://api.aoikujira.com/kawase/json/usd | jq .
{
  "result": "ok",
  "basecode": "USD",
  "update": "2015-07-13 17:41:44",
  "source": "*",
  "API_URL": "http://api.aoikujira.com/kawase/",
  ...(中略)
  "JPY": "123.44644",
  "KRW": "1131.46557",
  "HKD": "7.75138",
  ...(中略)
  "LBP": "1514.20449"
}
```

JPYに1ドルを円換算した値が入っているのが分かります。（他にもKRW（ウォン）やHKD（香港ドル）もありますね）
とりあえず、この値を抜き取って、引数で与えられた数値に掛けあわせるだけの、簡単なスクリプトを書いてみます。

```python:exchange1.py
#coding:utf-8

import sys
import urllib
import json

URL = "http://api.aoikujira.com/kawase/json/usd"

if __name__ == "__main__":
    price = float(sys.argv[1])

    response = urllib.urlopen(URL)
    data = json.loads(response.read())
    rate = float(data[u'JPY'])
    print "{0}(USD) => {1}(JPY)".format(1, rate)
    print "{0}(USD) => {1}(JPY)".format(price, price * rate)
```

```bash
$ python exchange1.py 2.5
1(USD) => 122.568(JPY)
2.5(USD) => 306.42(JPY)
```

引数で2.5（ドル）を与えると、306円という答えが返ってきました。
ついでに1ドルあたりの円も返しています。

目的達成ですね。
以上、終了です。

といきたいところなのですが、今回は公開したいので、もう少しマシにします。

## API叩き過ぎ防止
連続でAPI叩くのも良くないので、APIへのアクセスは1日1回にしようと思います。

```python
import urllib
import json

URL = "http://api.aoikujira.com/kawase/json/usd"
FPATH = "/tmp/exchange"


def readCache():
    with open(FPATH, 'r') as f:
        body = f.read()
    return body


def writeCache(body):
    with open(FPATH, 'w') as f:
        f.write(body)


def fetchRates():
    # fetch rate list from remote
    response = urllib.urlopen(URL)
    body = response.read()

    writeCache(body)

    return body
```

fetchRatesでAPIへアクセスし、writeCacheで内容をファイルに保存するようにします。
readCacheでファイルに保存された内容を読み取ります。

```python
import os.path
from datetime import date, datetime

FPATH = "/tmp/exchange"


def hasCache():
    if os.path.isfile(FPATH):
        d = date.today()
        today = datetime.combine(d, datetime.min.time())
        mtime = datetime.fromtimestamp(os.path.getmtime(FPATH))
        if mtime > today:
            return True
    return False

```

hasCacheでキャッシュファイルがあるかどうか確認し、最終更新日時が今日だったら、Trueを返します。
キャッシュファイルが無いか、キャッシュファイルの最終更新日時が今日より前だったら、Falseを返します。

合わせると、次のようになります

```python:exchange2.py
#!/usr/bin/env python

import sys
import os.path
from datetime import date, datetime
import urllib
import json

URL = "http://api.aoikujira.com/kawase/json/usd"
FPATH = "/tmp/exchange"


def hasCache():
    if os.path.isfile(FPATH):
        d = date.today()
        today = datetime.combine(d, datetime.min.time())
        mtime = datetime.fromtimestamp(os.path.getmtime(FPATH))
        if mtime > today:
            return True
    return False


def readCache():
    with open(FPATH, 'r') as f:
        body = f.read()
    return body


def writeCache(body):
    with open(FPATH, 'w') as f:
        f.write(body)


def fetchRates():
    # fetch rate list from remote
    response = urllib.urlopen(URL)
    body = response.read()

    writeCache(body)

    return body

if __name__ == "__main__":
    price = float(sys.argv[1])

    if hasCache():
        body = readCache()
    else:
        body = fetchRates()

    data = json.loads(body)
    rate = float(data[u'JPY'])
    print "{0}(USD) => {1}(JPY)".format(1, rate)
    print "{0}(USD) => {1}(JPY)".format(price, price * rate)

```

hasCacheがTrueを返したら、readCacheでキャッシュファイルから読み込み、Falseを返したら、fetchRatesでAPIから取得するようにしています。

## コマンドラインツールっぽくしたい

argparseを使って簡単にコマンドラインツールを作ることが出来ます

```python:options.py
#coding:utf-8

import argparse

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='convert usd to any currency.')
    parser.add_argument('-p', '--price', nargs='?', type=float, help='price', default=1.0)
    parser.add_argument('-c', '--currency', nargs='?', help='currency', default=u'JPY')
    parser.add_argument('-r', '--reverse', action='store_true', help='reverse the direction')
    parser.add_argument('-v', '--verbosity', action='count', help='increase output verbosity')

    args = parser.parse_args()
    price = args.price
    currency = args.currency
    reverse = args.reverse
    verbosity = args.verbosity

    print "price:",     price
    print "currency:",  currency
    print "reverse:",   reverse
    print "verbosity:", verbosity
```

上記を保存して、実行してみましょう

```bash
# 引数なし
$ python options.py
price: 1.0
currency: JPY
reverse: False
verbosity: None

# 引数つき
$ python options.py -p 2.5 -c EUR -r -vvv
price: 2.5
currency: EUR
reverse: True
verbosity: 3

# ヘルプメッセージが自動生成されている
$ python options.py -h
usage: options.py [-h] [-p [PRICE]] [-c [CURRENCY]] [-r] [-v]

convert usd to any currency.

optional arguments:
  -h, --help            show this help message and exit
  -p [PRICE], --price [PRICE]
                        price
  -c [CURRENCY], --currency [CURRENCY]
                        currency
  -r, --reverse         reverse the direction
  -v, --verbosity       increase output verbosity
```

簡単ですね!

ここまでの、すべてを合わせたものがGistに上がっています
https://gist.github.com/kei-sato/98675769952ec7538d6a

# まとめ
* Pythonを使ってWebAPIをコマンドラインツールに落としこむ流れを紹介しました
* urllibとjsonで簡単にAPIを取り扱うことが出来ます
* 過度にAPIを叩きすぎないようキャッシュをつけてあげると親切だと思います
* argparseで簡単にコマンドラインツールが作れます
* brew installも簡単にできます -> [Pythonで作ったコマンドラインツールをbrew install出来るようにする](http://qiita.com/kei-sato/items/276afa8dd8197fc91772)