Redmineのチケット量をグラフ化した話(Part4)(完)
前回までのところで、Elasticsearchにデータぶちこんで、Kibanaで表示できる所まで行けた。今回は、Redmineから終わってないチケットを取ってきて、それをElasticsearchにぶちこむ、という流れをスクリプトで作ってみたい。手段はなんでもいいとは思うのだが、Python3でやってみようと思う。
Redmineのチケット数を取得
まず、WEBブラウザでRedmineに対して http://localhost:3000/issues.json ってな感じで愚直にアクセス。
{"issues": (中略) "total_count":50,"offset":0,"limit":25}
そうすると、こんな感じでjson形式のレスポンスが返ってくる。この、total_count の部分が未消化チケットにあたるものだろう。なので、PythonからRedmineにHTTPアクセスして、レスポンスとして返ってきたjsonをパースしてやればなんとなくうまく行きそうだ。
# coding: UTF-8
import urllib.request
import json
tickets_url = "http://localhost:3000/issues.json"
res = urllib.request.urlopen(tickets_url) # HTTPアクセス
j = json.loads(res.read().decode("utf-8")) # jsonパース
tickets = j["total_count"] # total_countが未消化チケット数
# チケット数を表示してみる
print(tickets)
サクッと組んでみたところ、ちゃんと取得できた模様。
Elasticsearchへの登録
subprocessで外部プロセスのcurl呼び出してPOST処理…というのも考えたのだが、それだとなんか力技感が否めない。きっと、偉い人達がpython3向けのライブラリ作ってくれてるんじゃね?と思って調べてみたら、案の定elasticsearch-pyとやらが用意されている模様なので、pip なり easy_install なりでインストールしとく。
import datetime
from elasticsearch import Elasticsearch
host_name = "localhost"
host_port = 9200
es = Elasticsearch(host_name, port=host_port)
# 現在の日付を取得
now_date = datetime.datetime.now()
now_date_str = now_date.strftime("%Y-%m-%d")
# POST用のデータを作る
body = {
"tickets" : tickets,
"date" : now_date_str
}
# tasks/task にデータをぶっこむ
es.index(index="tasks", doc_type="task", body=body)
ここで死ぬほど試行錯誤するかと覚悟してたんだが、意外や意外、これでうまくデータが取り込めたようだ。さっきのスクリプトと組み合わせれば、チケット数と今日の日付をデータベースに登録する事ができた。こいつをJenkinsのジョブに登録して、週一ぐらいで呼び出すことでうまい具合にグラフが描画されるようになった。
グラフの撮影を自動化したい
しかし、これだけではまだ問題がある。集計結果のグラフが描画されても、それを手作業でスクリーンショット撮影しなければならないのだ。愚直に作業手順を考えてみると、WEBブラウザ開いて、ウインドウのサイズをいい感じに調整して、Alt+Printscreenして、mspaint開いてCtrl+Vで貼り付け、Ctrl+Sで所定の場所にpngで保存…とか地味にめんどくさい作業だ。といったところで、そんな作業はJenkin寿司に任せちゃいたいよね。
そこで色々とGoogle先生に調べてもらったところ、「selenium」なるものを使えばプログラムからWEBブラウザを意のままに操れて、スクリーンショットまで撮れちゃうらしいパねえ!seleniumを操れる環境は色々あるらしいんだが、マイフェイバリット言語であるpython3でやってみる事にした。
Seleniumをダウンロード
まず、このあたりからSeleniumをダウンロードしてくる。もちろん書いてあるとおりpipとかでよかですが、「selenium-某.tar.gz」をダウンロードしてきて、解凍して出てきたseleniumフォルダをpythonのインストールフォルダ直下のLibにぶっこむ方法でもOK(あんまり行儀よくないかもだが)。 C:\Python34\Lib\selenium ってな感じで。
WebDriverをダウンロード
俺は生粋のGoogle Chromeユーザーなのだが、プログラムからChromeを扱うにあたっては、Chrome専用のWEBドライバーなるものが必要らしい。
ということで、「Latest Release」のリンクから、「chromedriver.exe」をダウンロードしてくる。俺の意識の低さはとどまる所を知らないので、こいつもseleniumフォルダと同じ場所にぶちこんでみた。俺の場合は C:\Python34\Lib\selenium\chromedriver.exe ってな感じだが、実際どこ置いてもいいと思うぜ。
とりま動作確認
# coding: UTF-8
from selenium import webdriver
ch = webdriver.Chrome(executable_path=r"C:\Python34\Lib\selenium\chromedriver.exe")
ch.get(r"https://www.google.co.jp/")
ch.quit()
Googleパイセンで調べてきた内容をもとに、pythonでコードを書き殴ってみる。webdriverをimportしてきて、.Chromeの引数でexecutable_pathに、さっきぶちこんだexeのパスを指定してやる。あとは、getでURL指定して開くだけ。最後にquit()でブラウザを閉じる。
試しに Visual Studio Codeなんぞでステップ実行してみると、Chromeさんが「私、何者かに操られてます!」というアピールをしながらも、健気に動いてくれる姿を確認できる。
愚直に撮影してみる
ch.set_window_size(1000, 500) # ウインドウサイズ指定
ch.get(url) # URL開く
ch.save_screenshot(out_path) # 指定パスに画像保存
ウインドウサイズを指定して、URLを開いて、その画像を保存するようなスクリプトを組んでみた。これできっと、グラフ画像がいい感じに撮影できるはz・・・
アッー!ロード中の画面が撮影されてんじゃーん!
いったいどうすりゃいいんだぜ・・・!
試行錯誤した結果
色々と一筋縄ではいかんかったので、最終的に出来上がったソースを貼っておく。
まず、ローディング画面対策として、特定の要素が出現するまでウェイトする処理を入れてみた。上のドキュメントから引っ張ってきたのだが、ぶっちゃけ中身よく分かってない。なんとなく分かるのは、WebDriverWaitの第二引数がタイムアウト時間で、「By.某」の部分でidやらclassname指定するんだろうなー、って感じ。ブラウザのデバッグ機能(ChromeのF12とか)を使えば、要素は簡単に洗い出せる。
あとは小細工として、execute_scriptを挟んでみた。こいつはjavascriptを実行するやつなのだが、Kibanaの元ページに変なメニューバーが入ってたので、無理やりjQueryでCSS弄って消してみた。ちなみに、KibanaでビジュアライズしてあるグラフはURLのケツにembed=trueつければ全画面化するみたい。
結果、出来上がったのはこんな感じの画像。ゴリ押し感は否めないが、それとなく動くものはできたので、Jenkinsで定期的に呼び出してやって事なきを得た。
だいぶ長い道のりだったが、なんとかやりたい事は達成できた。めでたしめでたし。
振り返り
この一連の記事を別ブログで書いてたのがだいたい4年前なのだが、そこから1年ぐらいかけてRedmineのチケットを消化しおわり、上長に報告(頑張って働いてるよアピール)する必要もなくなったので、KibanaとElasticSearchの環境はひっそりと畳んでしまった。
むしろ、今ではRedmineすら畳んでチケット的なやつはGitHubクローンのIssueに移行してしまったのだが、また機会があればElasticSearchとKibanaの組み合わせを使って何かしらのデータを可視化してきたいわねー。
この記事が気に入ったらサポートをしてみませんか?