はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

Redmineのpluginの習作(wikiのマクロでsqlを発行し、それを埋め込む)

ことの経緯

Redminewikiページ中に、issuesテーブル等を特定の条件で検索して、それを一覧表示したいということがあったのだが、リッチなプラグインを探して試すよりも作成した方が早かったので作成してみた。

うろ覚えであるが、Redminewikiでオレオレマクロを作るとしたら次のような手順になる。

  1. init.rbというファイルをプラグインディレクトリに作成。
  2. init.rbファイルの中の、 Redmine::WikiFormatting::Macros.register do の中にプログラムを記載
  3. 「macro : マクロ名 do |obj,args,text| 」でマクロを定義。このmacroの返り値がwikiに出力される。
  4. obj、args、textは引数。

プラグイン チュートリアル - Redmineガイド

wikiでのマクロの呼び出しの基本の書式は、

{{マクロ名(引数)}}

なので、例えば後述のプラグインの例だと

{{isql2tagged_list(select attname from pg_attribute where attrelid = 'issues'::regclass)}}

とやるとこのSQLの結果がぶちまけられる。

プラグインのプログラム例

# -*- coding: utf-8 -*-
Redmine::Plugin.register :isql2table do
  name 'Isql2table plugin'
  author 'foo'
  description 'This is a plugin for Redmine'
  version '0.0.1'
  url 'http://example.com/path/to/plugin'
  author_url 'bar'
  
  Redmine::WikiFormatting::Macros.register do
    desc "与えられたsql(select文)を走行して表形式で出力する ※意図的だがSQLインジェクション相当のため取扱い注意"
    
    macro :isql2tagged_list do |obj,args,text|
      sqlstr = text.to_s
      str = ''
      if sqlstr =~ /^select/i then #select文のみ対応
        ActiveRecord::Base.connection.send(:select,sqlstr).each_with_index do |row,i|
          str = str + args[0]
          j = 1
          row.each do |k,v|
            str = str + v + args[j]
            j = j + 1
          end
        end
        str.html_safe
      end
    end
    
    macro :isql2table do |obj,args,text|
      str = ""
      dot = args.first
      wd = ' width="' + args[1] + '" '
      zero = args[2]
      under = args[3]
      eq = args[4]
      ov = args[5]
      ov2 = args[6]
      REG = Regexp.new('\A([1-9]\d{0,1}|[0])\z')
      # REG = Regexp.new('^\d{1,2}$')
      sqlstr = text.to_s
      if sqlstr =~ /^select/i then #select文のみ対応
        rslt = []
        #select文SQL発行
        ActiveRecord::Base.connection.send(:select,sqlstr).each_with_index do |row,i|
          rslt.push(row)
        end
        
        #列ごとの平均を求めるための前計算
        pivot_sum = {}
        pivot_cnt = {}
        pivot_sum.default = 0
        pivot_cnt.default = 0
        rslt.each_with_index do |row,i|
          row.each do |k,v|
            colval = v.to_s
            if colval =~ REG then #正規表現にマッチする場合のみ対象とする
              pivot_sum[k] = pivot_sum[k] + colval.to_i
              pivot_cnt[k] = pivot_cnt[k] + 1
            end
          end
        end
        
        #表を作成
        rslt.each_with_index do |row,i|
          #見出し行を出力
          if i == 0 then
            str = '<table><tr>'
            row.each do |k,v|
              str = str + '<th style="background-color:#00a; color:#fff;"' + wd + '><strong>' + k.to_s + '</strong></th>'
            end
          end
          str = str + '</tr><tr>'
          
          #結果行を出力
          row.each do |k,v|
            colval = v.to_s
            tdval = v.to_s
            sty = ''
            if colval =~ REG then
              #正規表現にマッチする場合、引数で指定の文字のアスキー棒グラフにする
              tdval = sprintf("% 3d",colval.to_i).gsub(/ /," ") + " " + dot * colval.to_i
              #列方向の平均と比べて、表の背景を着色する
              sb = colval.to_i - (pivot_sum[k]/pivot_cnt[k]).round
              sty = under if sb < 0
              sty = eq if sb == 0
              sty = ov if sb > 0
              sty = ov2 if sb - colval.to_i > 0 #約2倍以上だったら...に相当
              sty = zero if colval.to_i == 0
              sty = ' style="background-color:' + sty + ';"'
            end
            str = str + '<td ' + sty + '>' + tdval + '</td>'
          end
        end
      else
        str = "select文ではないので、なにもしません"
      end
      
      str = str + '</tr></table>'
      str.html_safe
      
    end
    
  end
  
end 

obj、args、textについて

公式 www.redmine.org

日本語でチュートリアル的にまとめされている方のページ (この方のページを最初に気づいていれば、自分のこの記事は作る必要なかったかもと今更。) torutk.hatenablog.jp

補足

今見ると、プラグインのプログラム中のisql2tableでは何がしたかったのかよくわからないが、

{{isql2table(>,100,#eee,#d6e685,#cd7,#bc6,#8cc665,) select tracker_id from issues }}

のようなメモが残っているので、色つけをしたかったのかもしれない。