From 722eaed7216270d8e89f50c92abb1cfaf9bf1503 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Fri, 14 Aug 2015 15:42:28 +0000 Subject: [PATCH] Adds support for custom date format when importing a CSV file (#950). git-svn-id: http://svn.redmine.org/redmine/trunk@14495 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/imports_helper.rb | 10 ++++++++++ app/models/import.rb | 16 ++++++++++++++++ app/models/issue_import.rb | 12 +++++++++--- app/views/imports/settings.html.erb | 4 ++++ test/fixtures/files/import_dates.csv | 4 ++++ test/functional/imports_controller_test.rb | 3 ++- test/unit/issue_import_test.rb | 14 ++++++++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/files/import_dates.csv diff --git a/app/helpers/imports_helper.rb b/app/helpers/imports_helper.rb index 63cffaca9..3af7520cf 100644 --- a/app/helpers/imports_helper.rb +++ b/app/helpers/imports_helper.rb @@ -30,4 +30,14 @@ module ImportsHelper name = "import_settings[mapping][#{field}]" select_tag name, options_for_mapping_select(import, field, options) end + + # Returns the options for the date_format setting + def date_format_options + Import::DATE_FORMATS.map do |f| + format = f.gsub('%', '').gsub(/[dmY]/) do + {'d' => 'DD', 'm' => 'MM', 'Y' => 'YYYY'}[$&] + end + [format, f] + end + end end diff --git a/app/models/import.rb b/app/models/import.rb index b7064d932..df29d1788 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -27,6 +27,14 @@ class Import < ActiveRecord::Base validates_presence_of :filename, :user_id validates_length_of :filename, :maximum => 255 + DATE_FORMATS = [ + '%Y-%m-%d', + '%d/%m/%Y', + '%m/%d/%Y', + '%d.%m.%Y', + '%d-%m-%Y' + ] + def initialize(*args) super self.settings ||= {} @@ -201,6 +209,14 @@ class Import < ActiveRecord::Base end end + def row_date(row, key) + if s = row_value(row, key) + format = settings['date_format'] + format = DATE_FORMATS.first unless DATE_FORMATS.include?(format) + Date.strptime(s, format) rescue s + end + end + # Builds a record for the given row and returns it # To be implemented by subclasses def build_object(row) diff --git a/app/models/issue_import.rb b/app/models/issue_import.rb index 2ff127605..7a7cf1e83 100644 --- a/app/models/issue_import.rb +++ b/app/models/issue_import.rb @@ -122,10 +122,10 @@ class IssueImport < Import attributes['parent_issue_id'] = parent_issue_id end end - if start_date = row_value(row, 'start_date') + if start_date = row_date(row, 'start_date') attributes['start_date'] = start_date end - if due_date = row_value(row, 'due_date') + if due_date = row_date(row, 'due_date') attributes['due_date'] = due_date end if done_ratio = row_value(row, 'done_ratio') @@ -133,7 +133,13 @@ class IssueImport < Import end attributes['custom_field_values'] = issue.custom_field_values.inject({}) do |h, v| - if value = row_value(row, "cf_#{v.custom_field.id}") + value = case v.custom_field.field_format + when 'date' + row_date(row, "cf_#{v.custom_field.id}") + else + row_value(row, "cf_#{v.custom_field.id}") + end + if value h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(value, issue) end h diff --git a/app/views/imports/settings.html.erb b/app/views/imports/settings.html.erb index 7afbb84d5..0bc87dd9c 100644 --- a/app/views/imports/settings.html.erb +++ b/app/views/imports/settings.html.erb @@ -17,6 +17,10 @@ <%= select_tag 'import_settings[encoding]', options_for_select(Setting::ENCODINGS, @import.settings['encoding']) %>

+

+ + <%= select_tag 'import_settings[date_format]', options_for_select(date_format_options, @import.settings['date_format']) %> +

<%= submit_tag l(:label_next).html_safe + " »".html_safe, :name => nil %>

<% end %> diff --git a/test/fixtures/files/import_dates.csv b/test/fixtures/files/import_dates.csv new file mode 100644 index 000000000..e94e2e24c --- /dev/null +++ b/test/fixtures/files/import_dates.csv @@ -0,0 +1,4 @@ +subject;start;due;custom +Valid dates;10/07/2015;12/08/2015;14/07/2015 +Invalid start date;04/15/2015;; +Invalid custom date;;;04/15/2015 diff --git a/test/functional/imports_controller_test.rb b/test/functional/imports_controller_test.rb index 5227274b4..6648676ed 100644 --- a/test/functional/imports_controller_test.rb +++ b/test/functional/imports_controller_test.rb @@ -69,13 +69,14 @@ class ImportsControllerTest < ActionController::TestCase import = generate_import post :settings, :id => import.to_param, - :import_settings => {:separator => ":", :wrapper => "|", :encoding => "UTF-8"} + :import_settings => {:separator => ":", :wrapper => "|", :encoding => "UTF-8", :date_format => '%m/%d/%Y'} assert_redirected_to "/imports/#{import.to_param}/mapping" import.reload assert_equal ":", import.settings['separator'] assert_equal "|", import.settings['wrapper'] assert_equal "UTF-8", import.settings['encoding'] + assert_equal '%m/%d/%Y', import.settings['date_format'] end def test_post_settings_should_update_total_items_count diff --git a/test/unit/issue_import_test.rb b/test/unit/issue_import_test.rb index 7665bf405..aee1af441 100644 --- a/test/unit/issue_import_test.rb +++ b/test/unit/issue_import_test.rb @@ -78,6 +78,20 @@ class IssueImportTest < ActiveSupport::TestCase assert_equal [false, true, false], issues.map(&:is_private) end + def test_dates_should_be_parsed_using_date_format_setting + field = IssueCustomField.generate!(:field_format => 'date', :is_for_all => true, :trackers => Tracker.all) + import = generate_import_with_mapping('import_dates.csv') + import.settings.merge!('date_format' => Import::DATE_FORMATS[1]) + import.mapping.merge!('subject' => '0', 'start_date' => '1', 'due_date' => '2', "cf_#{field.id}" => '3') + import.save! + + issue = new_record(Issue) { import.run } # only 1 valid issue + assert_equal "Valid dates", issue.subject + assert_equal Date.parse('2015-07-10'), issue.start_date + assert_equal Date.parse('2015-08-12'), issue.due_date + assert_equal '2015-07-14', issue.custom_field_value(field) + end + def test_run_should_remove_the_file import = generate_import_with_mapping file_path = import.filepath -- 2.39.5