Skip to content
This repository has been archived by the owner on Feb 2, 2018. It is now read-only.

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Junya Ogura committed May 27, 2009
0 parents commit f39996c
Show file tree
Hide file tree
Showing 12 changed files with 485 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.rdoc
@@ -0,0 +1,3 @@
= importer

Description goes here
247 changes: 247 additions & 0 deletions app/controllers/importer_controller.rb
@@ -0,0 +1,247 @@
require 'fastercsv'
require 'tempfile'

class ImporterController < ApplicationController
unloadable

before_filter :find_project

ISSUE_ATTRS = [:id, :subject, :assigned_to, :fixed_version,
:author, :description, :category, :priority, :tracker, :status,
:start_date, :due_date, :done_ratio, :estimated_hours]

def index
end

def match
# params
file = params[:file]
splitter = params[:splitter]
wrapper = params[:wrapper]
encoding = params[:encoding]

# save import file
@original_filename = file.original_filename
tmpfile = Tempfile.new("redmine_importer")
if tmpfile
tmpfile.write(file.read)
tmpfile.close
tmpfilename = File.basename(tmpfile.path)
if !$tmpfiles
$tmpfiles = Hash.new
end
$tmpfiles[tmpfilename] = tmpfile
else
flash[:error] = "Cannot save import file."
return
end

session[:importer_tmpfile] = tmpfilename
session[:importer_splitter] = splitter
session[:importer_wrapper] = wrapper
session[:importer_encoding] = encoding

# display sample
sample_count = 5
i = 0
@samples = []

FasterCSV.foreach(tmpfile.path, {:headers=>true, :encoding=>encoding, :quote_char=>wrapper, :col_sep=>splitter}) do |row|
@samples[i] = row

i += 1
if i >= sample_count
break
end
end # do

if @samples.size > 0
@headers = @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])
end
@project.all_issue_custom_fields.each do |cfield|
@attrs.push([cfield.name, cfield.name])
end
@attrs.sort!

end

def result
tmpfilename = session[:importer_tmpfile]
splitter = session[:importer_splitter]
wrapper = session[:importer_wrapper]
encoding = session[:importer_encoding]

if tmpfilename
tmpfile = $tmpfiles[tmpfilename]
if tmpfile == nil
flash[:error] = "Missing imported file"
return
end
end

default_tracker = params[:default_tracker]
update_issue = params[:update_issue]
unique_field = params[:unique_field]
journal_field = params[:journal_field]
update_other_project = params[:update_other_project]
ignore_non_exist = params[:ignore_non_exist]
fields_map = params[:fields_map]
unique_attr = fields_map[unique_field]

# check params
if update_issue && unique_attr == nil
flash[:error] = "Unique field hasn't match an issue's field"
return
end

@handle_count = 0
@update_count = 0
@skip_count = 0
@failed_count = 0
@failed_issues = Hash.new
@affect_projects_issues = Hash.new

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

project = Project.find_by_name(row[attrs_map["project"]])
tracker = Tracker.find_by_name(row[attrs_map["tracker"]])
status = IssueStatus.find_by_name(row[attrs_map["status"]])
author = User.find_by_login(row[attrs_map["author"]])
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"]])

# new issue or find exists one
issue = Issue.new
journal = nil
issue.project_id = project != nil ? project.id : @project.id
issue.tracker_id = tracker != nil ? tracker.id : default_tracker
issue.author_id = author != nil ? author.id : User.current.id

if update_issue
# custom field
if !ISSUE_ATTRS.include?(unique_attr.to_sym)
issue.available_custom_fields.each do |cf|
if cf.name == unique_attr
unique_attr = "cf_#{cf.id}"
break
end
end
end

if unique_attr == "id"
issues = [Issue.find_by_id(row[unique_field])]
else
query = Query.new(:name => "_importer", :project => @project)
query.add_filter("status_id", "*", [1])
query.add_filter(unique_attr, "=", [row[unique_field]])

issues = Issue.find :all, :conditions => query.statement, :limit => 2, :include => [ :assigned_to, :status, :tracker, :project, :priority, :category, :fixed_version ]
end

if issues.size > 1
flash[:warning] = "Unique field #{unique_field} has duplicate record"
@failed_count += 1
@failed_issues[@handle_count + 1] = row
break
else
if issues.size > 0
# found issue
issue = issues.first

# ignore other project's issue or not
if issue.project_id != @project.id && !update_other_project
@skip_count += 1
next
end

# ignore closed issue except reopen
if issue.status.is_closed?
if status == nil || status.is_closed?
@skip_count += 1
next
end
end

# init journal
note = row[journal_field] || ''
journal = issue.init_journal(author || User.current,
note || '')

@update_count += 1
else
# ignore none exist issues
if ignore_non_exist
@skip_count += 1
next
end
end
end
end

# project affect
if project == nil
project = Project.find_by_id(issue.project_id)
end
@affect_projects_issues.has_key?(project.name) ?
@affect_projects_issues[project.name] += 1 : @affect_projects_issues[project.name] = 1

# required attributes
issue.status_id = status != nil ? status.id : issue.status_id
issue.priority_id = priority != nil ? priority.id : issue.priority_id
issue.subject = row[attrs_map["subject"]] || issue.subject

# optional attributes
issue.description = row[attrs_map["description"]] || issue.description
issue.category_id = category != nil ? category.id : issue.category_id
issue.start_date = row[attrs_map["start_date"]] || issue.start_date
issue.due_date = row[attrs_map["due_date"]] || issue.due_date
issue.assigned_to_id = assigned_to != nil ? assigned_to.id : issue.assigned_to_id
issue.fixed_version_id = fixed_version != nil ? fixed_version.id : issue.fixed_version_id
issue.done_ratio = row[attrs_map["done_ratio"]] || issue.done_ratio
issue.estimated_hours = row[attrs_map["estimated_hours"]] || issue.estimated_hours

# custom fields
issue.custom_field_values = issue.available_custom_fields.inject({}) do |h, c|
if value = row[attrs_map[c.name]]
h[c.id] = value
end
h
end

if (!issue.save)
# 记录错误
@failed_count += 1
@failed_issues[@handle_count + 1] = row
end

if journal
journal
end

@handle_count += 1
end # do

if @failed_issues.size > 0
@failed_issues = @failed_issues.sort
@headers = @failed_issues[0][1].headers
end
end

private

def find_project
@project = Project.find(params[:project_id])
end

end
2 changes: 2 additions & 0 deletions app/helpers/importer_helper.rb
@@ -0,0 +1,2 @@
module ImporterHelper
end
23 changes: 23 additions & 0 deletions app/views/importer/index.html.erb
@@ -0,0 +1,23 @@
<h2><%=l(:label_issue_importer)%></h2>

<% form_tag({:action => 'match'}, {:multipart => true}) do %>
<%= hidden_field_tag 'project_id', @project.id %>

<p><label><%=l(:label_upload_notice)%></label><br/>
<%= file_field_tag 'file', :size => 60%></p>

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

<p><label><%=l(:label_upload_splitter)%></label>
<%= text_field_tag "splitter", ',', {:size => 3, :maxlength => 1}%></p>

<p><label><%=l(:label_upload_wrapper)%></label>
<%= text_field_tag "wrapper", '"', {:size => 3, :maxlength => 1}%></p>

</fieldset>

<%= submit_tag l(:button_upload) %>
<% end %>

76 changes: 76 additions & 0 deletions app/views/importer/match.html.erb
@@ -0,0 +1,76 @@
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'importer', :plugin => 'redmine_importer' %>
<% end %>

<h2><%= l(:label_match_columns) %></h2>
<!--
<fieldset class="box"><legend><%= l(:label_load_rules) %></legend>
<% @headers.each do |column| %>
<% end %>
</fieldset>
-->
<% form_tag({:action => 'result'}, {:multipart => true}) do %>
<%= hidden_field_tag 'project_id', @project.id %>
<fieldset class="box"><legend><%= l(:label_match_select) %></legend>
<% @headers.each do |column| %>
<label class="tabular"><%= column %>:
<%= select_tag "fields_map[#{column}]", "<option value=\"\">#{l(:option_ignore)}</option>" + options_for_select( @attrs ) %></label>
<% end %>
</fieldset>

<fieldset class="box"><legend><%= l(:label_import_rule) %></legend>
<label><%= l(:label_default_tracker) %>
<%= select_tag "default_tracker", options_from_collection_for_select(@project.trackers, 'id', 'name') %></label><br/>

<label><%= check_box_tag "update_issue", true, false %> <%= l(:label_update_issue) %>
</label><br/>
<%= observe_field("update_issue", :function => <<END_OF_STRING
document.getElementById("unique_field").disabled = !element.checked;
document.getElementById("journal_field").disabled = !element.checked;
document.getElementById("update_other_project").disabled = !element.checked;
document.getElementById("ignore_non_exist").disabled = !element.checked;
END_OF_STRING
)
%>

&nbsp;&nbsp;&nbsp;&nbsp;<label><%= l(:label_unique_field) %>
<%= select_tag "unique_field", options_for_select(@headers), {:disabled => true} %></label><br/>

&nbsp;&nbsp;&nbsp;&nbsp;<label><%= l(:label_journal_field) %>
<%= select_tag "journal_field", "<option value=\"\">#{l(:option_ignore)}</option>" + options_for_select(@headers), {:disabled => true} %></label><br/>

&nbsp;&nbsp;&nbsp;&nbsp;<label><%= check_box_tag "update_other_project", true, false, {:disabled => true} %>
<%= l(:label_update_other_project) %></label><br/>

&nbsp;&nbsp;&nbsp;&nbsp;<label><%= check_box_tag "ignore_non_exist", true, false, {:disabled => true} %>
<%= l(:label_ignore_non_exist) %></label><br/>

</fieldset>

<%= submit_tag l(:button_submit) %>
<!-- <%= submit_tag l(:button_save_rules_and_submit) %>
<%= text_field_tag('rule_name', l(:label_rule_name), {:size => 32, :maxlength => 32, :onfocus => "if(this.value == '#{l(:label_rule_name)}') {this.value = ''}"}) %>
--><br/>
<% end %>

<hr/>

<%= l(:label_toplines, @original_filename) %>
<table class="list">
<thead><tr>
<% @headers.each do |column| %>
<th><%= column %></th>
<% end %>
</tr></thead>
<tbody>
<% @samples.each do |issue| -%>
<tr class="<%= cycle("odd", "even") %>">
<% issue.each do |column| %><%= content_tag 'td', column[1] %><% end %>
</tr>
<% end %>
<tr class="<%= cycle("odd", "even") %>">
<% @headers.each do |column| %><td>...</td><% end %>
</tr>
</tbody>
</table>
31 changes: 31 additions & 0 deletions app/views/importer/result.html.erb
@@ -0,0 +1,31 @@
<% content_for :header_tags do %>
<%= stylesheet_link_tag 'importer', :plugin => 'redmine_importer' %>
<% end %>

<h2><%= l(:label_import_result) %></h2>
<p><%= l(:label_result_notice, @handle_count, @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/>
<% end %></p>
<hr/>

<% if @failed_count > 0 %>
<%= l(:label_result_failed, @failed_count) %>
<table class="list">
<thead><tr>
<th>#</th>
<% @headers.each do |column| %>
<th><%= column %></th>
<% end %>
</tr></thead>
<tbody>
<% @failed_issues.each do |id, issue| -%>
<tr class="<%= cycle("odd", "even") %>">
<td><%= id %></td>
<% issue.each do |column| %><%= content_tag 'td', column[1] %><% end %>
</tr>
<% end %>
</tbody>
</table>
<% end %>

0 comments on commit f39996c

Please sign in to comment.