General
CITROËN C3
PEUGEOT 405 Mi16x4
PEUGEOT 306 Style
NISSAN SKYLINE

Redmineとは

RedmineはRuby on Railsで開発されたWebベースのプロジェクト管理ツールです。
主要な使い方としてはバグ管理システム(BTS)でしょうか?
導入方法などはいろいろなサイトで紹介されていますので、ここでは割愛しますが、Ruby on Rails実行環境構築ができていれば簡単かと思います。
以下のサイトからダウンロードできます。
http://rubyforge.org/frs/?group_id=1850

Redmine Importerとは

Redmineは「チケット」という単位で管理しますが、新規チケットは1件ずつ登録していかなくてはなりません。
これですと、Excelなど他の管理方法から移行する場合大変な作業となってしまいますが、そんなときに便利なのが一括アップロードできるRedmine Importerプラグインです。

ダウンロードとインストール

以下のサイトからダウンロードし、解凍します。
https://github.com/juno/redmine_importer
作成されたフォルダ名を「redmine_importer」に変更して、
<Redmineルート>¥vendor¥plugins
にコピーします。

Redmine Importerの制限事項

Redmine Importerは管理者向けを想定しているのか、あまりユーザーに優しくなく以下のような制限があります。
  • UTF-8エンコードされたCSVファイルしかアップロードが成功しません。
  • CSVの項目中に改行コードあるとアップロードが成功しません。
  • 改行コードはLFでなくてはなりません。
  • アップロードファイル名を指定し忘れた場合などのエラーチェックがなく、Internal Server Errorとなります。
  • 日付項目の形式は'yyyy-mm-dd'でなければなりません。
  • Redmine上チェックボックスの項目はダウンロードした場合、「はい」「いいえ」となりますが(日本語の場合)、Redmine Importerでは、「はい」=1、「いいえ」=0としなくてはなりません。
  • 上記の制限にひっかかってアップロードが失敗した場合でも理由を表示してくれません。

S-JISファイルをインポートできるようにする

上で書いた制限事項すべてに対応するのは面倒ですが、通常CSVファイルをExcelで編集することが多いことを考えて、S-JISエンコードと項目中の改行コードに対応できるよう改修します。
エラーチェックについては別の機会に説明できればと思います。
日付などのフォーマットについては、Excelで各自対応することで...

<Redmineルート>¥vendor¥plugins¥redmine_importer¥app¥controllers¥importer_controller.rbを開くと、matchというメソッドがあります。この中身を次のように修正します。
  • このメソッドの4行目から記述されている、splitter、wrapper、encodingの値のセットを画面で入力された値を用いず、以下のように固定の値とします。どうせ強制的にUTF-8のCSVに変更しますので...。
  • このため、<Redmineルート>¥vendor¥plugins¥redmine_importer¥app¥view¥index.htmlの値のセット部分も削除してしまって構いません。
  • if tmpfileの後にtmpfile.write(file.read)という、アップロードしたファイルを読み込んで、そのまま一時ファイルに書き込む処理があります。この部分をtmpfile.write(conv_str(file.read))のようにconv_strメソッドを通してから書き込みを行うように修正します。
  •   def match
        # params 
        file = params[:file]
        splitter = ','
        wrapper = "\""
        encoding = 'U'
        
        # save import file
        @original_filename = file.original_filename
        tmpfile = Tempfile.new("redmine_importer")
        if tmpfile
    #      tmpfile.write(file.read)
          tmpfile.write(conv_str(file.read))
          tmpfile.close
    
  • importer_controller.rbの末尾に以下のようなconv_strメソッドを追加します。
  • このメソッドの中で行っていることは、以下のような内容です。
    • 文字コードをUTF-8に変換します。
    • 改行コードにCR(¥r)があった場合、削除します。
    • 最終行に改行コードがあった場合、削除します。
    • ダブルクォーテーション(")で囲まれた項目中の改行コード(¥n)とカンマ(,)を待避します。
    • 待避に用いる文字列はここでは'@@@@dn@@@@'等を使っていますが、通常使われない文字列であれば、何でも構いません。
    • ダブルクォーテーション(")を削除します。
    • 項目区切りのカンマをダブルクォーテーション付にします。
    • 行始めの項目と行末の項目をダブルクォーテーション付にします。
    • 待避した項目中の改行コードを元に戻します。
    • 待避した項目中のカンマを元に戻します。
    • 文字列の最初にダブルクォーテーション、最後にダブルクォーテーションと改行コードを付けます。
      def conv_str(str)
        # UTF-8に変換します。
        str_u = NKF.nkf('-w', str)
        # 改行コードにCR(\r)があった場合、\nに置き換えます。
        str_u=str_u.gsub("\r\n","\n")
        str_u=str_u.gsub("\r","\n")
        # 最終行に改行コードが合った場合、削除します。
        if str_u[str_u.length-1] == 10
          str_u=str_u[0...str_u.length-2]
        end
        # ダブルクォーテーション(")で囲まれた項目中の
        # 改行コード(¥n)とカンマ(,)を待避します。
        regexp=/[,][\"][^\"]*[\"][,]/
        i = 0
        flag = str_u.index(regexp, i)
        while flag != nil 
          temp_target=str_u[regexp]
          target = temp_target[1...temp_target.length-2]
          rep_str=target.gsub("\n",'@@@@dn@@@@')
          rep_str=rep_str.gsub(',','@@@@dc@@@@')
          str_u=str_u.gsub(target, rep_str)
          i = flag + 1
          flag = str_u.index(regexp, i)
        end
        # ダブルクォーテーションを削除します。
        str_u=str_u.gsub('"','')
        # 項目区切りのカンマを?
        # ダブルクォーテーション付にします。
        str_u=str_u.gsub(',','","')
        # 行始めの項目と行末の項目を
        # ダブルクォーテーション付にします。
        str_u=str_u.gsub("\n","\"\n\"")
        # 待避した項目中の改行コードを元に戻します。
        str_u=str_u.gsub('@@@@dn@@@@',"\n")
        # 待避した項目中のカンマを元に戻します。
        str_u=str_u.gsub('@@@@dc@@@@',",")
        # 文字列の最初にダブルクォーテーション、
        # 最後にダブルクォーテーションと改行コードを付けます。
        str_u='"'+str_u+'"'+"\n"
        return str_u
      end
    
これで、できあがりです!!