雑多な日々

ノンセクションで行こうかと思っています。

redmineでチケットをCSV形式でインポート

2010年10月15日 | Weblog
こちらをみると、juno's redmine_importer at master - GitHubを使って、簡単インポート!なんてあるので、挑戦。

が、1.0.2だと、なかなか手強かった。

まず、言語別のメッセージファイル(ja.yml)がそのままじゃ使えず。
1つは、ファイルの先頭に言語名を記述、以下の行はインデントを設定する必要がある、と。
もう1つは、ファイルの配置場所。lang/以下にあるファイルは、config/locales/以下に移動する必要あり。
これは、他のpluginとかでも、同様な話かも。
さらにもう1ついじりました。
なぜか、メッセージに対し、複数の変数を渡すと、文句を言ってくるので、メッセージに対し、1つの引数になるよう、メッセージを分割しました。
これらの設定変更で、メッセージはちゃんと、日本語でも表示されました。

次は、1.0.2だから、というわけじゃないと思うんですが、文字コードにしてやられました。
一応、最初の画面で文字コードを選択できるようにはなっているのですが、選んだところで、どうも、正しく動いていない。
いろいろ調べてみると、FasterCSVでファイルを読み込む時に、encodingは渡されているのですが、どうも、機能していない。
いろいろ、トライしてみました。
ファイルの読み込み時に一括してUTF-8に変換して、その文字列をFasterCSVに渡すものの、正しく解析できない模様。
なので、ファイルを読み込んだ後、個々のデータをちまちま変換する方法で。
nkfで文字コード変換を実行しているため、かなりの回数、nkfの実行モジュールが呼び出されていると思われます。
改善しようと思えば、できなくはないと思いますが、まずは動くものを、ということで。

それと、何ででしょうか、nkfのオプションでわざわざ入力の文字コードを指定していたのですが、nkfは、入力の文字コードは自動判定してくれるので、出力をUTF-8にしてね、とすればよいので、結局、最初の画面の文字コード指定は、意味がなくなりました。

まぁ、この文字コードにはやられましたが、動作結果は納得かな。

以下、unified diffを。
インデントがスペースか、tabの違いで、rejectされるかもしれませんので、ご注意を。

----
diff -uNr juno-redmine_importer-9505446/app/controllers/importer_controller.rb redmine_importer/app/controllers/importer_controller.rb
--- juno-redmine_importer-9505446/app/controllers/importer_controller.rb 2010-10-14 18:00:31.000000000 +0900
+++ redmine_importer/app/controllers/importer_controller.rb 2010-10-14 18:05:30.000000000 +0900
@@ -2,13 +2,14 @@
 require 'fastercsv'
 require 'tempfile'
 require 'nkf'
+require 'kconv'
 
 class ImporterController < ApplicationController
   unloadable
   
   before_filter :find_project
 
-  ISSUE_ATTRS = [:id, :subject, :assigned_to, :fixed_version,
+  ISSUE_ATTRS = [:identifier, :subject, :assigned_to, :fixed_version,
     :author, :description, :category, :priority, :tracker, :status,
     :start_date, :due_date, :done_ratio, :estimated_hours]
   
@@ -48,7 +49,13 @@
     i = 0
     @samples = []
     
-    FasterCSV.foreach(tmpfile.path, {:headers=>true, :encoding=>encoding, :quote_char=>wrapper, :col_sep=>splitter}) do |row|
+    FasterCSV.foreach(tmpfile.path, {:headers=>true, :encoding=>encoding, :quote_char=>wrapper, :col_sep=>splitter}) do |row_orig|
+
+      row = FasterCSV::Row.new(convert_headers_encoding(row_orig.headers), convert_headers_encoding(row_orig.headers), true)
+      for n in 0..(row.size - 1)
+        row[n] = row_orig[n] != nil ? NKF::nkf('-w', row_orig[n]) : row_orig[n]
+      end
+
       @samples[i] = row
       
       i += 1
@@ -58,13 +65,13 @@
     end # do
 
     if @samples.size > 0
-      @headers = convert_headers_encoding(@samples[0].headers, encoding)
+      @headers = convert_headers_encoding(@samples[0].headers)
     end
     
     # fields
     @attrs = Array.new
     ISSUE_ATTRS.each do |attr|
-      @attrs.push([l_has_string?("field_#{attr}".to_sym) ? l("field_#{attr}".to_sym) : attr.to_s.humanize, attr])
+      @attrs.push([l("field_#{attr}".to_sym), attr])
     end
     @project.all_issue_custom_fields.each do |cfield|
       @attrs.push([cfield.name, cfield.name])
@@ -112,7 +119,12 @@
     # attrs_map is fields_map's invert
     attrs_map = fields_map.invert
       
-    FasterCSV.foreach(tmpfile.path, {:headers=>true, :encoding=>encoding, :quote_char=>wrapper, :col_sep=>splitter}) do |row|
+    FasterCSV.foreach(tmpfile.path, {:headers=>true, :encoding=>encoding, :quote_char=>wrapper, :col_sep=>splitter}) do |row_orig|
+
+      row = FasterCSV::Row.new(convert_headers_encoding(row_orig.headers), convert_headers_encoding(row_orig.headers), true)
+      for n in 0..(row.size - 1)
+        row[n] = row_orig[n] != nil ? NKF::nkf('-w', row_orig[n]) : row_orig[n]
+      end
 
       project = Project.find_by_name(row[attrs_map["project"]])
       tracker = Tracker.find_by_name(row[attrs_map["tracker"]])
@@ -121,7 +133,7 @@
       priority = Enumeration.find_by_name(row[attrs_map["priority"]])
       category = IssueCategory.find_by_name(row[attrs_map["category"]])
       assigned_to = User.find_by_login(row[attrs_map["assigned_to"]])
-      fixed_version = Version.find_by_name(row[attrs_map["fixed_version"]])
+      fixed_version = Version.find_by_name_and_project_id(row[attrs_map["fixed_version"]], project.id)
   
       # new issue or find exists one
       issue = Issue.new
@@ -194,6 +206,7 @@
       # project affect
       if project == nil
         project = Project.find_by_id(issue.project_id)
+        fixed_version = Version.find_by_name_and_project_id(row[attrs_map["fixed_version"]], project.id)
       end
       @affect_projects_issues.has_key?(project.name) ?
         @affect_projects_issues[project.name] += 1 : @affect_projects_issues[project.name] = 1
@@ -236,7 +249,7 @@
     
     if @failed_issues.size > 0
       @failed_issues = @failed_issues.sort
-      @headers = convert_headers_encoding(@failed_issues[0][1].headers, encoding)
+      @headers = convert_headers_encoding(@failed_issues[0][1].headers)
     end
   end
 
@@ -246,11 +259,8 @@
     @project = Project.find(params[:project_id])
   end
 
-  def convert_headers_encoding(headers, encoding)
-    nkf_option = nil
-    nkf_option = '-Sw' if encoding == 'S'
-    nkf_option = '-Ew' if encoding == 'EUC'
-    nkf_option ? headers.map! {|e| NKF.nkf('-m0 -x ' + nkf_option, e)} : headers
+  def convert_headers_encoding(headers)
+    headers.map! {|e| NKF.nkf('-m0 -x -w', e)}
   end
 
 end
diff -uNr juno-redmine_importer-9505446/app/views/importer/index.html.erb redmine_importer/app/views/importer/index.html.erb
--- juno-redmine_importer-9505446/app/views/importer/index.html.erb 2010-10-14 18:00:31.000000000 +0900
+++ redmine_importer/app/views/importer/index.html.erb 2010-10-14 13:25:04.000000000 +0900
@@ -8,7 +8,7 @@
 
    <fieldset class="box"><legend><%= l(:label_upload_format) %></legend>
        <p><label><%=l(:label_upload_encoding)%></label>
-       <%= select_tag "encoding", "<option value=\"U\">UTF8</option><option value=\"EUC\">EUC</option><option value=\"S\">SJIS</option><option value=\"N\">None</option>" %></p>
+       <%= select_tag "encoding", "<option value=\"UTF-8\">UTF8</option><option value=\"EUC-JP\">EUC</option><option value=\"SJIS\">SJIS</option><option value=\"N\">None</option>" %></p>
        
        <p><label><%=l(:label_upload_splitter)%></label>
        <%= text_field_tag "splitter", ',', {:size => 3, :maxlength => 1}%></p>
diff -uNr juno-redmine_importer-9505446/app/views/importer/result.html.erb redmine_importer/app/views/importer/result.html.erb
--- juno-redmine_importer-9505446/app/views/importer/result.html.erb 2010-10-14 18:00:31.000000000 +0900
+++ redmine_importer/app/views/importer/result.html.erb 2010-10-14 17:54:33.000000000 +0900
@@ -3,7 +3,7 @@
 <% end %>
 
 <h2><%= l(:label_import_result) %></h2>
-<p><%= l(:label_result_notice, @handle_count, @handle_count - @failed_count) %></p>
+<p><%= l(:label_result_notice_f, @handle_count) %><%= l(:label_result_notice_b, @handle_count - @failed_count) %></p>
 <p><%= l(:label_result_projects) %><br/>
 <% @affect_projects_issues.each do |project, count|%>
 <label class="tabular2"><%= project %>:</label><label class="tabular2"><%= l(:label_result_issues, count) %></label><br/>
diff -uNr juno-redmine_importer-9505446/config/locales/en.yml redmine_importer/config/locales/en.yml
--- juno-redmine_importer-9505446/config/locales/en.yml 1970-01-01 09:00:00.000000000 +0900
+++ redmine_importer/config/locales/en.yml 2010-10-13 20:03:39.000000000 +0900
@@ -0,0 +1,35 @@
+en:
+  label_import: "Import"
+  label_issue_importer: "Issue Importer"
+  label_upload_notice: "Select a CSV file to import. Import file must have a header row."
+  label_upload_format: "File format settings"
+  label_upload_encoding: "Encoding:"
+  label_upload_splitter: "Field seperate char:"
+  label_upload_wrapper: "Field wrap char:"
+
+  label_load_rules: "Load saved rules"
+  label_toplines: "Refer to top lines of %s:"
+  label_match_columns: "Matching Columns"
+  label_match_select: "Select match field"
+  label_import_rule: "Import rules"
+  label_default_tracker: "Default tracker:"
+  label_update_issue: "Update exists issue"
+  label_journal_field: "Select field as journal:"
+  label_unique_field: "Select unique field for identify issue:"
+  label_update_other_project: "Allow update issues of other projects"
+  label_ignore_non_exist: "Ignore none exist issues"
+  label_rule_name: "Input rule name"
+
+  label_import_result: "Import Result"
+  label_result_notice: "%d issues processed. %d issues success imported."
+  label_result_notice_f: "%d issues processed. "
+  label_result_notice_b: "%d issues success imported."
+  label_result_projects: "Affected projects:"
+  label_result_issues: "%d issues"
+  label_result_failed: "Failed %d rows:"
+
+  option_ignore: "Ignore"
+
+  button_upload: "Upload File"
+  button_submit: "Submit"
+  button_save_rules_and_submit: "Save match rules and submit"
diff -uNr juno-redmine_importer-9505446/config/locales/ja.yml redmine_importer/config/locales/ja.yml
--- juno-redmine_importer-9505446/config/locales/ja.yml 1970-01-01 09:00:00.000000000 +0900
+++ redmine_importer/config/locales/ja.yml 2010-10-13 14:09:35.000000000 +0900
@@ -0,0 +1,35 @@
+ja:
+  label_import: "インポート"
+  label_issue_importer: "チケットインポーター"
+  label_upload_notice: "インポートするCSVファイルを選択してください。ファイルの最初にはヘッダ行が必要です。"
+  label_upload_format: "ファイル形式の設定"
+  label_upload_encoding: "文字コードエンコーディング:"
+  label_upload_splitter: "フィールド区切り文字:"
+  label_upload_wrapper: "フィールド囲み文字:"
+
+  label_load_rules: "Load saved rules"
+  label_toplines: "%sの先頭:"
+  label_match_columns: "列のマッチング"
+  label_match_select: "対応させるフィールドの選択"
+  label_import_rule: "インポートルール"
+  label_default_tracker: "デフォルトのトラッカー:"
+  label_update_issue: "既存のチケットを更新する"
+  label_journal_field: "履歴として使用するフィールド:"
+  label_unique_field: "チケットの特定に使うユニークなフィールド:"
+  label_update_other_project: "他プロジェクトのチケット更新を許可する"
+  label_ignore_non_exist: "存在しないチケットの指定を無視する"
+  label_rule_name: "ルール名を入力"
+
+  label_import_result: "インポート結果"
+  label_result_notice: "%d 件のチケットを処理し、%d 件のチケットをインポートしました。"
+  label_result_notice_f: "%d 件のチケットを処理し、"
+  label_result_notice_b: "%d 件のチケットをインポートしました。"
+  label_result_projects: "更新したプロジェクト:"
+  label_result_issues: "%d 件"
+  label_result_failed: "%d 個の列が失敗:"
+
+  option_ignore: "無視"
+
+  button_upload: "アップロード"
+  button_submit: "送信"
+  button_save_rules_and_submit: "マッチングルールを保存して次へ"
----

最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。