Redmineでチケットの一括インポート
ここのページにあるやつをカスタマイズした。
RedmineでチケットをCSVから一括登録/更新するプラグイン - エンジニアブログ - スカイアーク
といっても85%くらいは作り直したけど。オリジナルと比べて改善させたのは以下の点。
- カスタムフィールドにも(できる限り)柔軟に対応できる
- 列の順番を動的に指定できる
- 登録された結果がきちんと見れる
- 管理者権限を持ったユーザーじゃないと使えない
なんとか公開できないものかとオンラインストレージを調べてみたけど「これ」ってものが見つからない。(はてながこういうサービスすれば便利なんだけど)
ということで色々調べてたらそもそも、
これらの著作物の二次著作物を再配布することはできません。
http://www.skyarc.co.jp/engineerblog/entry/2635.html
とあるので、まずいのかも。どうしたものか。
追記:
時間ができたらExcelからでも読めるようにして、プラグインの構築からやって独自のものとして公開するかもしれない。オリジナルからかなり変えているので問題はない…と思う。
追記2:2009年5月29日
ざっくりと書いてみます。そのままでは動かないかもしれませんが、参考になれば幸甚です。
管理者権限を持ったユーザーじゃないと使えないようにする
これはinit.rbをいじるだけです。メニューにリンクを追加する記述に:if => Proc.new {User.current.admin?}を加えます。
menu :application_menu, :importer, { :controller => 'importer', :action => 'index' }, :caption => 'importer', :if => Proc.new {User.current.admin?}
列の順番を動的に指定できるようにしてカスタムフィールドにもできる限り柔軟に対応する
ちょっと泥くさいですが、以下を参考にImporterController#executeを変更します。
def to_utf8(src) return src unless src Iconv.conv('UTF-8', l(:general_csv_encoding), src) end def execute # Current user @user = User.current # uploaded file @file = params[:file] # Create issues @status_msg = '' @datas = CSV::IOReader.parse(@file) @row = @datas.shift columns = [] @row.each do |r| header = to_utf8(r) case header when l(:field_status) columns << :status when l(:field_project) columns << :project when l(:field_tracker) columns << :tracker when l(:field_priority) columns << :priority when l(:field_subject) columns << :subject when l(:field_assigned_to) columns << :assigned_to when l(:field_category) columns << :category when l(:field_fixed_version) columns << :fixed_version when l(:field_author) columns << :author when l(:field_start_date) columns << :start_date when l(:field_due_date) columns << :due_date when l(:field_done_ratio) columns << :done_ratio when l(:field_estimated_hours) columns << :estimated_hours when l(:field_description) columns << :description else if IssueCustomField.find_by_name(header) columns << header else #error when not found in custom_fields @status_msg << "Unknown column : #{header}, #{l(:general_csv_encoding)}, #{Kconv.guess(r)}" return end end end @issues = Array.new @line_count = 0 @datas.each_with_index do |data, index| issue = Issue.new @line_count += 1 if data[data.size - 1] == nil || data[data.size - 1].empty? @status_msg << "Line #{index} : error. description required." next end status = project = tracker = priority = subject = assigned_to = nil category = fixed_version = author = start_date = due_date = done_ratio = nil estimated_hours = description = nil custom_fields = {} data.each_with_index do |d,i| case columns[i] when :status status = IssueStatus.find_by_name(to_utf8(d)) when :project project = Project.find_by_name(to_utf8(d)) when :tracker tracker = Tracker.find_by_name(to_utf8(d)) when :priority priority = Enumeration.find_by_name(to_utf8(d)) when :subject subject = to_utf8(d) when :assigned_to assigned_to = User.find_by_login(to_utf8(d)) when :category category = IssueCategory.find_by_name(to_utf8(d)) when :fixed_version fixed_version = Version.find_by_name(to_utf8(d)) when :author author = User.find_by_login(to_utf8(d)) when :start_date start_date = d when :due_date due_date = d when :done_ratio done_ratio = d when :estimated_hours estimated_hours = d when :description description = to_utf8(d) else f = IssueCustomField.find_by_name(columns[i]) case f.field_format when 'user_list' custom_fields[f.id] = User.find_by_login(to_utf8(d)).login when 'string' custom_fields[f.id] = to_utf8(d) else custom_fields[f.id] = d end end end unless issue or project or tracker or status @status_msg << "error" next end issue.status_id = status.id issue.project_id = project.id issue.tracker_id = tracker.id issue.priority_id = priority ? priority.id : '' issue.subject = subject issue.assigned_to_id = assigned_to ? assigned_to.id : '' issue.category_id = category ? category.id : '' issue.fixed_version_id = fixed_version ? fixed_version.id : '' issue.author_id = author ? author.id : nil issue.start_date = start_date issue.due_date = due_date issue.done_ratio = done_ratio issue.estimated_hours = estimated_hours issue.description = description issue.custom_field_values = custom_fields if issue.save @issues << issue else @status_msg << "Line #{index} : error" end end end
登録された結果をきちんと見れるようにする
execute.html.erbに次の行を追加します。ImporterController#execute内の処理で@issuesに作成したチケットが入ってることが必要です。
<table> <thead><tr> <th>#</th> <th><%= l(:field_status) %></th> <th><%= l(:field_project) %></th> <th><%= l(:field_tracker) %></th> <th><%= l(:field_priority) %></th> <th><%= l(:field_subject) %></th> <th><%= l(:field_assigned_to) %></th> <th><%= l(:field_category) %></th> <th><%= l(:field_fixed_version) %></th> <th><%= l(:field_author) %></th> <th><%= l(:field_start_date) %></th> <th><%= l(:field_due_date) %></th> <th><%= l(:field_done_ratio) %></th> <th><%= l(:field_estimated_hours) %></th> <th><%= l(:field_description) %></th> <% @issues && @issues.first && @issues.first.available_custom_fields.each do |f| %> <th><%= f.name %></th> <% end %> </tr></thead> <tbody> <% @issues && @issues.each do |issue| %> <tr> <td><%= issue.id %></td> <td><%= issue.status.name %></td> <td><%= issue.project.name %></td> <td><%= issue.tracker.name %></td> <td><%= issue.priority.name %></td> <td><%= issue.subject %></td> <td><%= issue.assigned_to && issue.assigned_to.name %></td> <td><%= issue.category && issue.category.name %></td> <td><%= issue.fixed_version && issue.fixed_version.name %></td> <td><%= issue.author.name %></td> <td><%= issue.start_date %></td> <td><%= issue.due_date %></td> <td><%= issue.done_ratio %></td> <td><%= issue.estimated_hours %></td> <td><%= issue.description[0..17] %>...</td> <% issue.available_custom_fields.each do |f| %> <td><%= issue.custom_value_for(f) %></td> <% end %> </tr> <% end %> </tbody> </table>