1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Redmine wikiマクロを作成する方法3(ステータスごとのチケットの割合をグラフで表示)

Last updated at Posted at 2024-12-22

Redmine wikiマクロを作成する方法3(ステータスごとのチケットの割合をグラフで表示)

Redmine Advent Calendar 2024の10日目の記事として作成しました。

image.png

今年のRedmine Advent Calendarに空白が多く残っており、クリスマスが過ぎる前までになんとか空白の日付の記事を書いて、空白の日付を埋めることを優先して記事を作成しました。そのため、濃くない内容の記事になるかもしれませんが、ご容赦ください。

完成形

下図のようにwikiマクロの実行結果が表示されます。
image.png


背景

Redmineには自作のwikiマクロを作成して登録することができます。
その方法としてはプラグインを作成しますがwikiマクロの登録のためのプラグインは比較的開発が簡単であるため、プラグインを作ったことがない方でも簡単に試してみることができるメリットがあります。

wikiマクロでは色々な処理をすることができるためRedmineの構造を理解したり、Rubyを理解したり、プラグインの仕組みを理解したりするためには良い教材になるかと思います。
今年のRedmine Advent Calendarを通して、何回かに分けてwikiマクロの作り方を紹介したいと思います。

今回の記事では「Redmine wikiマクロを作成する方法3」として「ステータスごとのチケットの割合をグラフで表示」する方法を説明します。

前提条件:

使用するwikiマクロ登録用プラグインはRedmine wikiマクロを作成する方法1で説明したプラグインを使用します。/plugins/wiki_macro_plugin/init.rbの「init.rb」ファイルにwikiマクロを登録します。


1. マクロを実装

  1. /plugins/wiki_macro_plugin/init.rbの「init.rb」ファイルを編集して、Wikiマクロを登録します:
    Redmine::Plugin.register :wiki_macro_plugin do
     省略
    end
    
     Redmine::WikiFormatting::Macros.register do
       desc "Display progress bars for each status in a project with clickable links to issue lists"
       macro :progress_bar_by_status do |obj, args|
         project = Project.find_by_identifier(args.first || obj.project.identifier)
         return "プロジェクトが見つかりません。" unless project
     
         # プロジェクト内のチケットを取得
         issues = project.issues
         return "チケットがありません。" if issues.empty?
     
         # ステータスごとの件数を取得
         total_count = issues.count
         status_counts = issues.group(:status).count
     
         # プログレスバーを生成
         content = "#{project.name}のステータスごとの進捗<br>"
         content << "<table border='1' style='border-collapse: collapse; width: 100%;'>"
         content << "<tr><th>ステータス</th><th>件数</th><th>割合</th><th>進捗バー</th></tr>"
     
         status_counts.each do |status, count|
           percentage = (count.to_f / total_count * 100).round(2)
           progress_bar = "<div style='width: 100%; background-color: #e0e0e0; border: 1px solid #ccc;'>
                             <div style='width: #{percentage}%; background-color: #4caf50; text-align: center; color: white;'>
                               #{percentage}%
                             </div>
                           </div>"
     
           # ステータスへのリンクを生成
           status_link = link_to(status.name, project_issues_path(project, set_filter: 1, f: ['status_id'], op: { 'status_id' => '=' }, v: { 'status_id' => [status.id] }))
     
           content << "<tr>"
           content << "<td>#{status_link}</td>"
           content << "<td>#{count}</td>"
           content << "<td>#{percentage}%</td>"
           content << "<td>#{progress_bar}</td>"
           content << "</tr>"
         end
     
         content << "</table>"
         content.html_safe
       end
     end
    

以下、コードを1行ずつ解説します。


Redmine::WikiFormatting::Macros.register do
  • 説明: RedmineにカスタムWikiマクロを登録するための開始宣言です。Redmine::WikiFormatting::Macros.registerメソッドにブロックを渡すことで、新しいマクロを定義できます。

  desc "Display progress bars for each status in a project with clickable links to issue lists"
  • 説明: マクロの説明文を指定しています。この内容は、{{macro_list}}を実行するとマクロの説明の欄に情報が反映されます。

{{macro_list}}を実行画面
image.png

{{macro_list}}を実行した画面でdescの内容が表示
image.png


  macro :progress_bar_by_status do |obj, args|
  • 説明: :progress_bar_by_statusという名前のマクロを定義します。objはWikiページやプロジェクトなどのコンテキストオブジェクト、argsはマクロに渡された引数を表します。

    project = Project.find_by_identifier(args.first || obj.project.identifier)
  • 説明: 引数からプロジェクト識別子を取得し、そのプロジェクトをデータベースから検索します。引数がない場合、現在のプロジェクトの識別子を使います。

    return "プロジェクトが見つかりません。" unless project
  • 説明: プロジェクトが見つからない場合、メッセージを返して処理を終了します。

    issues = project.issues
  • 説明: 指定したプロジェクトに関連するすべてのチケット(Issue)を取得します。

    return "チケットがありません。" if issues.empty?
  • 説明: プロジェクトにチケットがない場合、メッセージを返して処理を終了します。

    total_count = issues.count
  • 説明: チケットの総数をカウントして、total_countに代入します。

    status_counts = issues.group(:status).count
  • 説明: チケットをステータスごとにグループ化し、各ステータスの件数を取得します。

    content = "#{project.name}のステータスごとの進捗<br>"
  • 説明: 出力するHTMLコンテンツの開始部分を定義しています。プロジェクト名を表示するテキストを追加しています。

    content << "<table border='1' style='border-collapse: collapse; width: 100%;'>"
  • 説明: HTMLテーブルの開始タグを追加します。テーブルの幅は100%、セルのボーダーが崩れないようスタイルを設定しています。

    content << "<tr><th>ステータス</th><th>件数</th><th>割合</th><th>進捗バー</th></tr>"
  • 説明: テーブルのヘッダー行を追加しています。各列の名前(ステータス、件数、割合、進捗バー)を定義しています。

    status_counts.each do |status, count|
  • 説明: 各ステータスとその件数について、繰り返し処理を行います。

      percentage = (count.to_f / total_count * 100).round(2)
  • 説明: 各ステータスのチケット割合を計算し、小数点2桁に丸めます。

      progress_bar = "<div style='width: 100%; background-color: #e0e0e0; border: 1px solid #ccc;'>
                        <div style='width: #{percentage}%; background-color: #4caf50; text-align: center; color: white;'>
                          #{percentage}%
                        </div>
                      </div>"
  • 説明: HTMLとインラインCSSでプログレスバーを生成しています。バー全体を灰色にし、進捗部分を緑色で表示しています。

      status_link = link_to(status.name, project_issues_path(project, set_filter: 1, f: ['status_id'], op: { 'status_id' => '=' }, v: { 'status_id' => [status.id] }))
  • 説明: ステータス名をリンクに変換しています。リンク先は該当ステータスのチケット一覧ページです。

以下、このコード行を詳しく解説します:


1. link_to メソッド

  • 役割: Railsのlink_toメソッドは、HTMLの<a>タグを生成するために使います。

  • 構文:

    link_to(link_text, url, html_options = {})
    
    • link_text: リンクに表示する文字列。
    • url: リンクの宛先(今回の場合、チケットリストページのURL)。
    • html_options: 必要に応じて追加のHTML属性(今回使われていない)。
  • この例では:

    • link_text = status.name(ステータス名)
    • url = project_issues_path(...)で生成されるURL。

2. project_issues_path

  • 役割: 特定のプロジェクトのチケットリストのURLを生成します。

  • 構文:

    project_issues_path(project, params = {})
    
    • project: リンク先のプロジェクトオブジェクト。
    • params: URLに付加するパラメータ。
  • 今回の例では、以下のようなパラメータを追加しています。


2.1 set_filter: 1

  • 役割: チケットリストのフィルターを有効化します。
  • 効果: URLでフィルター条件を指定した場合、このパラメータが必要です。

2.2 f: ['status_id']

  • 役割: フィルターの対象フィールドを指定します。
  • 詳細:
    • 'status_id': ステータスIDをフィルタリング条件に設定。

2.3 op: { 'status_id' => '=' }

  • 役割: フィルター条件での演算子を指定します。
  • 詳細:
    • 'status_id' => '=': ステータスIDが特定の値に等しいという条件を意味します。

2.4 v: { 'status_id' => [status.id] }

  • 役割: フィルター条件の値を指定します。
  • 詳細:
    • 'status_id' => [status.id]:
      • status.idは現在のステータスオブジェクトのID。
      • ステータスIDがstatus.idと一致するチケットをフィルタリングします。

3. 全体の動作

  • 生成されるリンク:
    • status.nameがリンクのテキスト(例: "Open", "Closed"など)。
    • リンク先はproject_issues_pathで生成されたURL。具体例として次のような形式になります:
      /projects/example_project/issues?set_filter=1&f[]=status_id&op[status_id]==&v[status_id][]=2
      
      • 上記は、「ステータスIDが2のチケットのみを表示」という条件をURLで表現したものです。

4. 結果

このコードによって、次の動作が実現されます:

  • ユーザーがリンクをクリックすると、選択したステータス(例: "Open", "Closed"など)に対応するチケットリストが表示されます。

      content << "<tr>"
  • 説明: テーブルの新しい行を開始します。

      content << "<td>#{status_link}</td>"
  • 説明: ステータス名(リンク)をテーブルのセルとして追加します。

      content << "<td>#{count}</td>"
  • 説明: ステータスごとの件数をテーブルのセルとして追加します。

      content << "<td>#{percentage}%</td>"
  • 説明: 割合をテーブルのセルとして追加します。

      content << "<td>#{progress_bar}</td>"
  • 説明: プログレスバーをテーブルのセルとして追加します。

      content << "</tr>"
  • 説明: テーブルの行を終了します。

    end
  • 説明: ステータスごとの繰り返し処理を終了します。

    content << "</table>"
  • 説明: テーブルの閉じタグを追加します。

    content.html_safe
  • 説明: HTMLコンテンツをhtml_safeメソッドでマークし、HTMLのエスケープを防ぎます。

  end
end
  • 説明: マクロ定義ブロックの終了部分です。

2. マクロの使い方:

wikiページで下記の形式でwikiマクロを実行します。

 {{progress_bar_by_status(project_identifier)}}
  • project_identifierにプロジェクトの識別子を引数として渡します。引数が無い場合は、該当のwikiが所属しているプロジェクトがprogress_bar_by_statusに渡されます。

引数は有っても無くても動作します。
image.png

下図のようにwikiマクロの実行結果が表示されます。(上の出力結果が引数がない場合で、下の出力結果が引数が有る場合です。)
image.png

「ステータス」の欄のステータス名をクリックすると、下図のようにチケット一覧ページに移動できます。
image.png


感想

  • wikiマクロ登録用のプラグインがあれば、簡単にwikiマクロを登録できることが分かります。
  • 各ステータスごとのチケット件数の割合をグラフで表示でき、どのステータスのチケットが多いのかがグラフから簡単に把握できます。
  • 各ステータスごとのチケット件数の取得など、Redmineプラグインで必要な要素技術の習得に寄与します。
  • 出力結果が表形式で出力され、可読性の高い出力が可能。
  • 表の各欄で、降順、昇順で並べ替えができます。並べ替えると他の欄も連動して並べ変わります。件数の多い順に並べることもできます。
    image.png

この記事の作成者の紹介


山崎進

  • Redmine、Jquery、JavaScript,Rails、Ruby、SQL、VBA、RPAの開発を行なっています。
  • 自動化、業務の効率化に高い関心があります。
  • 下記の媒体で情報を発信しています。Redmineのプラグインの開発などに対応が可能ですので、お気軽にご連絡ください。

 * Qiita:https://qiita.com/ankosoft
 * Twitter:https://twitter.com/yamasaki24
 * Redmine Advent Calendarで記事投稿
 * redmine.tokyoで講演多数
 * Redmine Japan Vol.1 前夜祭、Redmine Japan Vol.3で講演
 * https://ankosoft.co.jp/blog/
 * https://technology.ankosoft.co.jp/


関連記事(2024年のRedmine Advent Calendarの記事です。)

* Redmine wikiマクロを作成する方法1

* Redmine wikiマクロを作成する方法2(完了チケットの割合をグラフで表示)

* Redmine wikiマクロを作成する方法3(ステータスごとのチケットの割合をグラフで表示)

* Redmine wikiマクロを作成する方法4(ステータスごとのチケットの割合を一つのグラフで表示)

* Redmine wikiマクロを作成する方法5(特定のユーザーのチケット状態の確認)

関連記事(2023年のRedmine Advent Calendarの記事です。)

* JqueryでRedmineのメニューにアイコンを入れる方法

* JqueryでRedmineのメニューを閉じたり開いたりする方法

 * JqueryでRedmineの「活動ページ」をもっと便利に(タイトルを開閉したり、曜日を入れたり)

 * JqueryでRedmineの「活動ページ」をもっと便利に②(フィルタリング機能と移動機能)

 * JqueryでRedmineの「wikiページ」をもっと便利に(フィルタリング機能と移動機能)

 * JqueryでRedmineの「チケットページ」をもっと便利に(年ごと、月ごと、四半期ごとの集計機能の開発)

 * RedmineのパスワードやログインIDを忘れた時の復旧方法

 * 「rails console」を使ってRedmine上のスケジュールを一括変更する方法

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?