0
0

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マクロを作成する方法4(ステータスごとのチケットの割合を一つのグラフで表示)

Last updated at Posted at 2024-12-22

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

Redmine Advent Calendar 2024の12日目の記事として作成しました。
image.png

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

完成形

前回のRedmine wikiマクロを作成する方法3(ステータスごとのチケットの割合をグラフで表示)は下図のように各ステータス毎に独立したグラフとして表示させましたが、

image.png

今回の開発では、下図のように、ステータスごとのチケットの割合を一つのグラフで表示されます。
image.png

多少の違いはありますが、今回の開発は前回の開発と重複したソースコードが多く存在します。


背景

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

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

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

前提条件:

使用する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 a single progress bar with sections for each status in a project"
       macro :combined_progress_bar 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
     
         # プログレスバーを作成
         progress_bar = "<div style='width: 100%; display: flex; border: 1px solid #ccc; background-color: #e0e0e0;'>"
     
         # 各ステータスごとにバーのセクションを作成
         status_counts.each do |status, count|
           percentage = (count.to_f / total_count * 100).round(2)
           color = "##{Digest::MD5.hexdigest(status.name)[0..5]}" # ステータス名を元に色を生成
           progress_bar << "<div style='width: #{percentage}%; background-color: #{color}; text-align: center; color: white; font-size: 0.8em;' title='#{status.name}: #{count}件 (#{percentage}%)'>"
           progress_bar << "#{percentage}%"
           progress_bar << "</div>"
         end
     
         progress_bar << "</div>"
     
         # プログレスバーの説明を追加
         legend = "<ul style='list-style: none; padding: 0; margin: 10px 0;'>"
         status_counts.each do |status, count|
     
           # ステータスへのリンクを生成
           status_link = link_to(status.name, project_issues_path(project, set_filter: 1, f: ['status_id'], op: { 'status_id' => '=' }, v: { 'status_id' => [status.id] }))
           
           color = "##{Digest::MD5.hexdigest(status.name)[0..5]}"
           legend << "<li style='margin: 5px 0;'>"
           legend << "<span style='display: inline-block; width: 15px; height: 15px; background-color: #{color}; margin-right: 5px;'></span>"
     
           legend << "#{status_link}: #{count}件"
           legend << "</li>"
         end
         legend << "</ul>"
     
         (progress_bar + legend).html_safe
       end
     end
    

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


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

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

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

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


  macro :combined_progress_bar do |obj, args|
  • 説明: :combined_progress_barという名前のWikiマクロを定義します。
  • 引数:
    • 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
  • 説明: チケットをステータスごとにグループ化し、各ステータスの件数をカウントします。

    progress_bar = "<div style='width: 100%; display: flex; border: 1px solid #ccc; background-color: #e0e0e0;'>"
  • 説明: プログレスバー全体のHTML要素の開始タグを作成します。
    • flexレイアウト: 各ステータスセクションを横並びに表示します。

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

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

      color = "##{Digest::MD5.hexdigest(status.name)[0..5]}"
  • 説明: ステータス名を元にMD5ハッシュを計算し、16進数の先頭6文字を取り出して色コードとして使用します。これにより、ステータスごとに一意な色を生成します。

      progress_bar << "<div style='width: #{percentage}%; background-color: #{color}; text-align: center; color: white; font-size: 0.8em;' title='#{status.name}: #{count}件 (#{percentage}%)'>"
  • 説明: プログレスバーのセクションをHTMLで生成します。
    • width: セクションの幅はステータス割合(percentage)に比例。
    • background-color: ステータス固有の色。
    • title: マウスオーバーで詳細(ステータス名、件数、割合)が表示されます。
      image.png

      progress_bar << "#{percentage}%"
  • 説明: セクション内に割合をテキスト表示します。

      progress_bar << "</div>"
  • 説明: プログレスバーセクションの終了タグを追加します。

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

    progress_bar << "</div>"
  • 説明: プログレスバー全体の終了タグを追加します。

    legend = "<ul style='list-style: none; padding: 0; margin: 10px 0;'>"
  • 説明: プログレスバーの説明(凡例)用のHTMLリストの開始タグを作成します。

    status_counts.each do |status, count|
  • 説明: 各ステータスについて繰り返し処理を開始します(凡例作成用)。

      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"など)に対応するチケットリストが表示されます。

      color = "##{Digest::MD5.hexdigest(status.name)[0..5]}"
  • 説明: 進捗バーで使用したのと同じ色を生成します。

      legend << "<li style='margin: 5px 0;'>"
  • 説明: 凡例リストの項目を開始します。

      legend << "<span style='display: inline-block; width: 15px; height: 15px; background-color: #{color}; margin-right: 5px;'></span>"
  • 説明: 各ステータスに対応する色付きの四角(凡例アイコン)を生成します。

      legend << "#{status_link}: #{count}件"
  • 説明: ステータス名(リンク)と件数を凡例リスト項目として追加します。

      legend << "</li>"
  • 説明: 凡例リストの項目を終了します。

    end
  • 説明: 凡例作成用の繰り返し処理を終了します。

    legend << "</ul>"
  • 説明: 凡例リストの終了タグを追加します。

    (progress_bar + legend).html_safe
  • 説明: プログレスバーと凡例を結合し、HTMLをエスケープせずにそのまま出力できるようにします。

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

2. マクロの使い方:

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

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

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

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

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


感想

  • wikiマクロ登録用のプラグインがあれば、簡単にwikiマクロを登録できることが分かります。
  • 各ステータスごとのチケット件数の割合を一つのグラフで表示でき、どのステータスのチケットが多いのかがグラフから簡単に把握できます。
  • 各ステータスごとのチケット件数の取得など、Redmineプラグインで必要な要素技術の習得に寄与します。
  • 該当のステータスのチケット一覧へ移動できるリンクが有り、簡単に該当のチケット一覧を表示させることが可能。
  • グラフの色を動的に作成できるメリットが有る。ただし、似たような色になってしまうデメリットが有り、今後、改善の余地はある。

この記事の作成者の紹介


山崎進

  • 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上のスケジュールを一括変更する方法

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?