• このエントリーをはてなブックマークに追加

ss-1365242724

はじめに

Redmine でチケットを切ったり簡単な Note を追記したりするのに毎度ブラウザで操作するのがアレなので,YAPC::Asia 2012 でも紹介されていた Redmine::Chan を使って IRC 経由で操作しています.

私の場合,ちょこちょこ「作業時間」(まぁテキトーにw)を記録していますが,現時点では Redmine::Chan はそのあたりをサポートしていません.そのため,Redmine::Chan 導入後もしばらくの間は,時間の記録についてはブラウザで行ってきましたが,さすがにもう限界な感じがしてきました.(Redmine,あまり使うモチベーション上がらんですよ,使い勝手的な意味で.)

そんなわけで「なければ作ればいいじゃない」の精神の下,Redmine::Chan をいくらかいじって,作業時間を記録できるようにしてみました.

その前に個人的 Tips

起動スクリプトにおける Redmine::Chan のコンストラクタのパラメータ issue_fields の要素に spent_hours を含める:

my $minechan = Redmine::Chan->new(
    ...
    issue_fields => [qw/... spent_hours ...],
    ...
);

ことで,「作業時間」を表示させることができます:

ss-1365245075


ただし,本家の仕様上,時間が 0 のものは表示されません.


pull request が取り込まれました:

できるようになったこと

通常の Note の場合

通常,Note の追記には,次の書式で行います:

通常の Note > #1557

こんな感じですね:

ss-1365242140

Note の書式を拡張

次のような書式に対応することで,作業時間の記録を可能にしました:

[{hours} {activity} {comments}] {note} > #{issue_id}  # 作業時間・Note ともに記録
[{hours} {activity}] {note} > #{issue_id}             # 上記のコメントなしバージョン
[{hours} {activity} {comments}] > #{issue_id}         # 作業時間のみ
[{hours} {activity}] > #{issue_id}                    # 作業時間のみ: コメントなし

[...] で囲っている部分に,時間記録に関する事項を入れることで,その内容を Redmine API 経由で POST する感じです.

実行例

例えば,次のように入力した場合:

[.25 開発 作業時間記録のコメント]  作業時間を記録したよ. > #1557

こんな感じでレスポンスがあります:

ss-1365244612

チケットの概要の「作業時間の記録」部分が更新されました:

ss-1365242233

Note も追記され:

ss-1365242251

作業時間とともにコメントもついていることがわかります:

ss-1365242284

さらに次々と記録してみます:

ss-1365242724

Note はこんな感じ:

ss-1365242774

時間記録の一覧はこんな感じになりました:

ss-1365242483

変更点のポイント

変更の詳細

次をご参照ください:

作業時間を記録するための API

リンク先は XML についての記述しかありませんが,リクエスト先の拡張子を .json に置き換えればたぶん通じます.

この仕様を参考に,Recmine::Chan::API に,create_time_entry というメソッドを追加しました:

sub create_time_entry {
    my ($self, $issue_id, $date, $hours, $activity, $comments) = @_;
    my ($activity_id) = $self->detect_activity_id($activity);
    if (! $activity_id) {
        warn qq{activity "$activity" could not be mapped to activity_id.};
    }
    if (! defined $date) {
        my @ymd = (localtime(time))[5,4,3];
        $ymd[0] += 1900;
        $ymd[1] += 1;
        $date = sprintf '%04d-%02d-%02d', @ymd;
    }
    return $self->post(
        'time_entries.json',
        Content_Type => 'application/json',
        Content => encode_json +{
            key => $self->api_key_as($self->who),
            time_entry => {
                issue_id    => $issue_id,
                spent_on    => $date,
                hours       => $hours,
                activity_id => $activity_id,
                comments    => $comments,
            }
        },
    );
}

メソッド detect_activity_id も私が追加したものですが,引数に渡した「活動の名前」に対応する,Redmine システム上の activity_id を返すものです.

活動(activity)種別に関する情報の取得

作業時間の記録には,「活動」の種別の指定が必須です.API 上でも,activity_id を指定する必要があります.

この「活動」に関するマスタ的(?)情報は,次から持ってくることができます:

{your_redmine_base}/enumerations/TimeEntryActivities.json?key={your_api_key}

次のような JSON が返ります:

ss-1365241490

(クエリパラメータ key を指定しない場合,Basic 認証になるようです.)

こんな感じで「活動」に関する情報を取得できることがわかったので,Redmine::Chan::API#reload に,名前と ID との対応情報を準備するような処理を追記しました:

sub reload {
    ...
    {
        # activity の定義を取得して,既存設定に混ぜ込む
        my $uri = 'enumerations/TimeEntryActivities.json';
        my $data = (eval {
            $self->get( $uri => { key => $self->api_key_as($self->who) } )->parse_response;
        } || +{})->{time_entry_activities} || [];
        my %hash = %{$self->activity_commands || +{}};
        for my $i (@$data) {
            my ($id, $name) = @$i{qw/id name/};
            $hash{$id} = [@{$hash{$id} || []}, $name];
        }
        $self->activity_commands_mixed(\%hash);
    }
}

活動の名前の対応づけ

私が個人的に使っている Redmine では,「設計作業」「開発作業」「連絡・伝達」といった名前で「活動」を設定しています.

しかし,IRC で作業時間を記録する際に,毎回「開発作業」とか「連絡・伝達」とか入力するのは非常にメンドイです.そこで,別名と本来の activity_id とを対応付けることで,そんなメンドさを回避できるようにしています.

Redmine::Chan のコンストラクタのパラメータに新しく, activity_commands というものを追加しました.パラメータ status_commands と同じ感じで定義できます:

my $minechan = Redmine::Chan->new(
    ...
    activity_commands => {
        1 => [qw/ 設計 /],              # 設計作業 / id:1
        2 => [qw/ 開発 /],              # 開発作業 / id:2
        3 => [qw/ 連絡 伝達 メール /],  # 連絡・伝達 / id:3
    },
);

こうすることで,

[1.5 連絡・伝達 メールを書いた] > #1557

とするところを,

[1.5 メール メールを書いた] > #1557

のように少し簡単にでき,いずれの場合も,「連絡・伝達」という活動として時間が記録されます.

個人的に勉強になったこと

Web API 等でパラメータ不備によるエラーを返す際,HTTP ステータス 422 Unprocessable Entity を返せばよいのですね.

おわりに

以上,IRC から Redmine を操作できる便利ツール Redmine::Chan を少しいじって作業時間の記録を行えるようにした,という俺得機能に関する経緯のご紹介でした.

あと,ちょっと調子に乗って,本家に pull request を送ってみました><