Backlog」タグアーカイブ

[Ruby] Backlogの作業時間の記録を半自動化する

前提

以下の環境で開発、動作確認

要素 バージョン
debian 8.6
ruby 2.2.2
gem 2.4.5
bundle 1.13.4
backlog_kit 0.15.0

リポジトリ
https://github.com/Sa2Knight/BacklogTimeKeeper

以下の続きのようなもの
[Ruby] BacklogをCUIで操作してみる

概要

BacklogAPIを用いて、コマンド実行のみで課題の作業時間を記録するCUIツールを作ったので紹介。

以下のように、コマンドで課題キーを指定して課題を開始。課題が完了したら再度コマンドを実行すると、作業時間を計算して、課題の作業時間を更新してくれる。それだけ。

目新しい技術要素もないが、ブログネタが無くなってきたので

10:08:39$ bruby Main.rb -s DEVS-1
プログラムを書く (DEV-1)を開始しました
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13:14:08$ bruby Main.rb -e
プログラムを書く (DEV-1)を終了し、作業時間を3.09時間追加しました

BacklogAPIの準備

BacklogAPIは、特別な申請なしにすぐに利用を開始することができる。BacklogAPIを利用したいBacklogにログインし、個人設定→API設定に移動すると、以下の画面になるので、適当に名前を付けてAPIを発行する。APIキーは後ほど必要になるので控えておく。

ツールの導入

リポジトリからソースコードを取得

GithubからCloneする。

$ git clone git@github.com:Sa2Knight/BacklogTimeKeeper.git
Cloning into 'BacklogTimeKeeper'...
remote: Counting objects: 48, done.
remote: Compressing objects: 100% (33/33), done.
remote: Total 48 (delta 23), reused 37 (delta 12), pack-reused 0
Receiving objects: 100% (48/48), 7.10 KiB | 0 bytes/s, done.
Resolving deltas: 100% (23/23), done.
$ cd BacklogTimeKeeper/

必要なライブラリをインストール

サードパーティ製のbacklog_kitを利用するので、bundleでインストールする。

$ bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/..........
Fetching version metadata from https://rubygems.org/..
Fetching dependency metadata from https://rubygems.org/.
Resolving dependencies...
Installing concurrent-ruby 1.0.5
Installing i18n 0.8.4
Installing minitest 5.10.2
Installing thread_safe 0.3.6
Installing multipart-post 2.0.0
Using bundler 1.13.4
Installing tzinfo 1.2.3
Installing faraday 0.10.1
Installing activesupport 5.1.1
Installing faraday_middleware 0.10.1
Installing backlog_kit 0.15.0
Bundle complete! 1 Gemfile dependency, 11 gems now installed.
Bundled gems are installed into ./vendor/bundle.

環境変数を設定

本ツールでは、BacklogのスペースID及びAPIキーを環境変数から参照する仕様になっているので、以下のようにエクスポートしておく。エクスポートする値は各々の環境にあわせたものにする。

$ export BACKLOG_SPACE="hoge"
$ export BACKLOGAPI="ghaiogapigipeghpghapghpheapiwthpiat"

使い方

課題の作業を開始する

Main.rbを-sオプションを付与して、対象の課題キーを指定して実行する。bundle execは必須だが、面倒なのでaliasつけたりするとマシになる。

$ bundle exec ruby Main.rb -s DEV-1
プログラムを書く(DEV-1)を開始しました

課題を開始すると、同ディレクトリにworkというファイルが作成されるが、これは対象の課題キーとその開始時間を記録しているだけのJSONファイルなので特に気にしなくて良い

$ cat work
{"key":"DEV-1","datetime":"2017-06-06 20:44:05 +0900"}

課題の作業を終了する

開始から数時間たったことにして、課題を終了する。終了時はMain.rbを-eオプションを付与して実行するだけで良い。前述のworkファイルで対象の課題キーが管理されているので、ここでは課題キーを指定する必要がない。

$ bundle exec ruby Main.rb -e
プログラムを書く(DEV-1)を終了し、作業時間を1.71時間追加しました

課題を確認すると1.71時間設定されているのがわかる

また、既に作業時間が記録されている課題に対して同様に作業開始→作業終了のコマンドを実行すると、以下のように作業時間を追加する形で更新される

使いみち

まだたったコレだけしかできないが、個人的には既に以下のような実用性がある

  • 課題の作業時間を自分で時刻見て計算する必要がなくなる
  • 億劫だった作業時間記録作業がむしろ楽しみになった
  • 本ツールで課題を開始するとその課題にだけしっかりと集中できるようになった気がする(複数課題並行できないのはむしろメリット)

今後

多分以下のように発展されられる気がする

  • 本日の作業時間の集計機能(どの課題にどのぐらい時間を使ったか)
  • 作業中の課題をチャットワークで通知(名前欄に動的に記入したり?)
  • 課題のステータス変更との組み合わせ(課題終了時に、ステータスを「完了」にするとか)

とにかくBacklogAPIとチャットワークAPIのマッシュアップが夢広がる。

[Ruby] BacklogをCUIで操作してみる

前提

要素 バージョン
Debian 8.6
Ruby 2.2.2
gem 2.4.5
bundle 1.13.4
Backlog API 2.9.0
BacklogKit 0.15.0

また、Backlogの個人設定より、APIキーを生成していることを前提とする

概要

国産プロジェクト管理ツールであるBacklogを、ブラウザではなく、BacklogAPIを用いて、スクリプト経由で操作する。スクリプトの記述には、APIを間接的に利用するライブラリであるBacklogKitを用いるためにRubyを採用する。

本記事で作成するツールでは、主に以下のことをCUIで実現する

  • ユーザ情報の取得
  • 課題の作成(タイトル/本文のみ指定可能)
  • 課題の操作(ステータスの変更/完了理由の変更/実作業時間の変更)

実用的にするにはさらにカスタマイズする必要があるが、本記事では基本的なAPI利用が出来るようになる部分までにする。また、本課題での動作確認は、個人利用のために開設しているBacklogスペースを用いる。

BacklogAPI利用の準備

今回は、BacklogAPIをRubyで簡易的に利用するためのライブラリである、BacklogKitを利用する。BacklogKitはgemで管理されているので、bundleを用いてインストールする。

※ gem: Rubyのパッケージングシステム
※ Bundle: gemパッケージを管理するツール

Gemfileに以下のように記述

source "https://rubygems.org"
gem 'backlog_kit'

Bundleでインストール

bundle install --path bundle/vendor

スペースIDとAPIキーを記述したファイルを作成

BacklogAPIを利用するためには、対象のスペースIDと、APIキーが必要になる。スクリプト中にハードコーディングしても良いが、本記事で実装するツールは、Githubにてコードを公開しているため、APIキーが漏洩することを避けるために別途JSONファイルに記述することにする(もちろん該当のJSONファイルはリポジトリで共有しない)

ということで、以下のようなsecret.jsonを作成する。もちろんAPI_KEYはダミー

{
  "space_id": "saknight",
  "api_key": "hogehogefugafugafoofoobarbar",
  "project_key": "DEV"
}

とりあえず汎用的なメソッドを持たせることを目的に、Util.rbを作成し、そこにJSONファイルをロードするメソッドを実装する

require 'json'
class Util
  def self.load_secret_file
    File.open('secret.json') do |file|
      JSON.load(file)
    end
  end
end

これでBacklogAPIを利用する下準備は完了

BacklogAPIを使ってみる

Backloger.rbを作成し、ここにBacklogAPIを利用するコードを書いていく。とりあえずBacklogAPIを試しに使ってみるということで、ユーザ情報を取得してみる。

require 'backlog_kit'
require_relative 'Util'

secret = Util.load_secret_file
client = BacklogKit::Client.new(
  space_id: secret['space_id'],
  api_key: secret['api_key']
)

p client.get_space.body

bundleを使っているため、上記ファイルを以下のように実行する

$ bundle exec ruby Backloger.rb

以下のような実行結果が得られた

$ bundle exec ruby Backloger.rb
#<BacklogKit::Resource:0x007f9534806850 @attributes={:spaceKey=>"saknight", :name=>"saknight", :ownerId=>85748, :lang=>"ja", :timezone=>"Asia/Tokyo", :reportSendTime=>"18:00:00", :textFormattingRule=>"backlog", :created=>"2017-03-04T15:47:40Z", :updated=>"2017-03-04T15:47:40Z"}>

どうやらBacklogKitではAPIの実行結果はBacklogKit::Resourceクラスのオブジェクトとして返却されるらしい。詳しい使い方はドキュメントやソースコードを追う必要があるが、以下のようにコードを変更すればスペースIDが取得できそうだ。

p client.get_space.body.spaceKey

実行結果

$ bundle exec ruby Backloger.rb
"saknight"

とりあえずBacklogkitを用いたBacklogAPIの利用が正常にできていることがわかった。あとはドキュメントやソースコードを読んで本ツールの目的を達成するためのコードを作成していく

課題の作成に必要なパラメータの取得

リファレンスによると、課題の追加には以下のパラメータが必須となる

  • 課題の名前
  • プロジェクトID
  • 課題の種別ID
  • 課題の優先度ID

ここで面倒なのが、プロジェクト/種別/優先度が全てIDを要求されていることだ。
例えば優先度の場合「高」「中」「低」といった表示名で指定するのではなく、それに対応するIDが必要になる。面倒なことにIDはBacklog環境のカスタマイズ次第で変わってしまうので、今回はスクリプトから動的に取得できるようにすることにする。

プロジェクトID/種別ID/優先度IDを動的に取得するために、以下のIdentifierクラスを作成した

require 'backlog_kit'

class Identifier

  def initialize(client)
    @client = client
  end

  def project(projectKey)
    project = @client.get_projects.body.find do |pb|
      pb.projectKey == projectKey
    end
    project.id
  end

  def priority(label)
    priority = @client.get_priorities.body.find do |pr|
      pr.name == label
    end
    priority.id
  end

  def issueType(project_key, label)
    type = @client.get_issue_types(project_key).body.find do |it|
      it.name == label
    end
    type.id
  end
end

上記クラスのメソッドは、それぞれプロジェクトキー、優先度、種別を指定すると、それに対応するIDを返却してくれる。
例えば以下のようなコードを実行すると

identifier = Identifier.new(client)
p identifier.projectId("DEV")
p identifier.priorities('中')
p identifier.issueTypeId("DEV", 'タスク')

以下のようにそれぞれIDに変換して出力してくれる

$ bundle exec ruby Backloger.rb
38382
3
172585

もちろんIDは基本的に不変であるため、毎回動的に取得する必要はなく、一度取得したら適当にキャッシュするなどすれば簡単に高速化することができるが、ここでは割愛する。

課題の作成

ようやく本題。前項で、課題の作成に必要なパラメータを動的に取得できるようになったので、create_issueメソッドを用いて、課題を作成する。以下のコードでは、「課題テスト」という、優先度が「中」で、種別が「タスク」の課題を作成する(他の設定項目は未設定状態)

client.create_issue('課題テスト', {
  :projectId   => identifier.project(secret['project_key']),
  :priorityId  => identifier.priority('中'),
  :issueTypeId => identifier.issueType(secret['project_key'], 'タスク'),
})

スクリプト実行後、課題が生成されていることが確認できる

課題の編集に必要なパラメータの取得

今回は、前項で作成した課題に対して、以下の編集を行う。

  • ステータスを「完了」にする
  • 完了理由を「対応済み」にする
  • 実績時間を「1時間」にする
  • 適当なコメントを投稿する

“課題の作成に必要なパラメータの取得”同様、ステータス/完了理由は、対応するIDが必要になるので、同様にIdentifierクラスに以下のメソッドを追加する。

  def status(label)
    status = @client.get_statuses.body.find do |st|
      st.name == label
    end
    status.id
  end

  def resolution(label)
    resolution = @client.get_resolutions.body.find do |res|
      res.name == label
    end
    resolution.id
  end

以下のコードを実行すると

identifier = Identifier.new(client)

p identifier.status('完了')
p identifier.resolution('対応済み')

以下のように完了と対応済みに対応するIDが取得できる

$ bundle exec ruby Backloger.rb
4
0

課題の編集

前項で課題編集のためのIDが取得できたので、update_issueメソッドを用いて、以下のように課題の修正を行う

client.update_issue('DEV-47', {
  :statusId => identifier.status('完了'),
  :resolutionId => identifier.resolution('対応済み'),
})

実行すると、以下のように課題が修正されたことが確認できる

所感

  • Backlogを普段から利用してるが、どうにも定型的な操作が億劫だと思っていたので、色々自動化してみたいと思いBacklogAPIの利用を初めてみた
  • リファレンスを見る限りほぼ全ての操作をAPIで利用できるので、極端に言えばブラウザを一切使わずにBacklogを利用することもできそう
  • 本記事ではBacklogAPI及びBacklogKitについて軽く触れた程度だが、ここから実用的なツールの開発を進め、一段落したところでまた記事を書ければなと思う
  • ここ1ヶ月資格試験に追われてなかなかプログラミング出来てなかったので、リハビリがてら楽しくコーディングできた
  • Rubyを使うのも久しぶりだが、リスト操作の柔軟さはやはりRubyならでは。気持ちよく使える