torutkのブログ

ソフトウェア・エンジニアのブログ

Redmineプラグイン作成でHookを使ってみる(続々)

前回(Redmineプラグイン作成でHookを使ってみる(続) - torutkのブログ)は、RedmineプラグインでView hooksの実装(HTMLテンプレート)において、コントローラークラスのインスタンス変数を利用する方法について記述しました。

今回は、既存のモデルをHTMLテンプレート内で利用する方法を調べてみました。

プロジェクトの概要ページのチケットトラッキング

Redmineで[概要]メニューを開くと、「チケットトラッキング」が表示されます。

しかし、ステータスごとの件数ではなく、未完了か完了かで集計されてしまいます。完了は、通常は[終了]または[却下]のステータスとなり、それ以外は未完了となります。

ステータス毎の集計を見たい場合、標準ではその画面が見当たらないので、プラグインを作って対応してみます。

チケットの件数を取得する方法

Railsでは、モデルにcountというメソッドがあり、SQLのSELECT COUNT(*)相当の機能となります。
最初、HTMLテンプレート(.erb)では、モデルへのアクセスが出来ないと思い、コントローラを作ってみましたが、試行錯誤してみたところできることが分かりました。

Redmine標準のプロジェクトの概要ページの表示では、次の方法でチケットの完了/未完了/合計の件数を取得しています。

  • redmine/app/controllers/projects_controller.rb
class ProjectsController < ApplicationController
    :
  def show
      :
    cond = @project.project_condition(Setting.display_subprojects_issues?)
    @open_issues_by_tracker = Issue.visible.open.where(cond).group(:tracker).count
    @total_issues_by_tracker = Issue.visible.where(cond).group(:tracker).count

チケットのモデルクラスIssueを使って、指定したトラッカーのチケットで、アクセス可能なチケットのオープン(未完了)のチケット件数と合計のチケット件数を取得しています。

visibleは、アクセスしているユーザーが閲覧できるチケットを対象としています。

whereの指定方法には幾つかの形式があり悩みました。
あちこちを見て回ったところ、次のようにハッシュ形式で条件を入れるのが分かりやすく簡潔でよさそうです。

  • redmine/app/models/issue_status.rb
Issue.where(:status_id => id, :closed_on => nil)

groupを使うとグループに指定した属性ごとの集計ができ、属性名をハッシュのキーとして値が取り出せるハッシュが得られるようです。
上述の例では、@open_issues_by_tracker[tracker] という式で指定したトラッカーの未完了の件数を取り出しています。

プロジェクトの概要のView hooksでステータス毎のチケット件数を表示する

フック名は、:view_projects_show_left になります。そこで、先日作成したプラグインのhook.rbに追記します。

  • plugins/redmine_issues_kpi/lib/hooks.rb
module RedmineIssuesKpi
  class Hooks < Redmine::Hook::ViewListener
      render_on :view_issues_index_bottom, :partial => 'issuesIndexBottom'
      render_on :view_projects_show_left, :partial => 'projectsShowLeft'
  end
end

パーシャル(HTMLテンプレート)を作成します。

  • plugins/redmine_issues_kpi/views/projects/_projectsShowLeft.html.erb
<h3>チケット数内訳</h3>
<% if @trackers.present? %>
  <% @trackers.each do |tracker| %>
    <h4><%= tracker.name %></h4>
    <table class="list">
      <thead>
        <tr>
          <% tracker.issue_statuses.each do |status| %>
            <th><%= status.name %></th>
          <% end %>
        </tr>
      </thead>
      <tbody>
        <tr>
          <% num_issues_by_status = Issue.visible.where(:project_id => @project.id, :tracker_id => tracker.id).group(:status).count %>
          <% tracker.issue_statuses.each do |status| %>
            <td><%= num_issues_by_status[status].to_i %></td>
          <% end %>
        </tr>
      </tbody>
    </table>
  <% end %>
<% end %>

とりあえずべたに記述してみました。
コントローラー(redmine/app/controllers/projects_controller.rb)で定義されたインスタンス変数を活用しています。

HTMLテンプレートでは、ロジックの記述に<% ... %>と、<%= ... %>があり、前者はロジックの実行(評価)だけを行い、後者はロジックの評価をHTMLに置き換えます。

@trackers にあるトラッカーそれぞれについて、ステータス毎のチケット件数をIssueクラスのメソッドで取得しています。

画面

今回のView hooksを入れた結果の画面を次に示します。


意外と使えるView hooks

View hooksを入れるビューの対象となるコントローラーにインスタンス変数が用意されていれば、かなりのことをView hooksで入れ込むことができます。

ただし、複雑なロジックをHTMLテンプレートに入れると非常に見苦しくなってしまうので、その場合はプラグインにコントローラーを設けてロジックをコントローラーに持たせるのがよいでしょう。

もしかすると、コントローラーでなくヘルパークラスを用意しても実装できるかもしれません(未確認)。