diff --git a/app/assets/javascripts/attachments.js.coffee b/app/assets/javascripts/attachments.js.coffee index db6fc07..e9dfc1e 100644 --- a/app/assets/javascripts/attachments.js.coffee +++ b/app/assets/javascripts/attachments.js.coffee @@ -7,7 +7,8 @@ jQuery -> data: '_method': 'put', 'attachment[col_sep]': $('#attachment_col_sep').val(), - 'attachment[quote_char]': $("#attachment_quote_char").val() + 'attachment[quote_char]': $("#attachment_quote_char").val(), + 'attachment[encoding]': $("#attachment_encoding").val() , success: (data) -> insertFields(data) false @@ -24,6 +25,7 @@ jQuery -> authenticity_token: $("input[name='authenticity_token']").val() col_sep: $("#attachment_col_sep").val() quote_char: $("#attachment_quote_char").val() + encoding: $("#attachment_encoding").val() , onComplete: (id, fileName, data) -> insertFields(data) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 07157e4..91ee4ff 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -14,3 +14,4 @@ ul.top_menu li:first-child a{margin-left:0} ul.top_menu li:last-child{border-right:none} hr.seperator{border-top:1px solid #ccc;background:#ccc;margin:5px 0} .clear{clear:both} +form label span.required{color: red; font-size: 110%; margin-left: 4px;} diff --git a/app/controllers/attachments_controller.rb b/app/controllers/attachments_controller.rb index 171fdf9..e855a1e 100644 --- a/app/controllers/attachments_controller.rb +++ b/app/controllers/attachments_controller.rb @@ -2,7 +2,7 @@ class AttachmentsController < ApplicationController load_and_authorize_resource def new - @attachment = Attachment.new(col_sep: ',', quote_char: '"') + @attachment = Attachment.new(col_sep: ',', quote_char: '"', encoding: 'utf-8') end # TODO: @@ -11,7 +11,7 @@ def new # - find or construct a row with all data set, so we can show examples def create # puts params[:file].tempfile.external_encoding.name - @attachment = Attachment.new(uploaded_data: params[:file], col_sep: params[:col_sep], quote_char: params[:quote_char]) + @attachment = Attachment.new(uploaded_data: params[:file], col_sep: params[:col_sep], quote_char: params[:quote_char], encoding: params[:encoding]) @attachment.user = current_user @attachment.save! render json: {success: true, id: @attachment.id, rows: @attachment.rows(4)}, status: :ok @@ -20,7 +20,8 @@ def create def update if @attachment.update_attributes(params[:attachment]) respond_to do |format| - format.html { redirect_to new_attachment_mapping_url(@attachment) } + format.html { redirect_to (@attachment.mapping.blank? ? new_attachment_mapping_url(@attachment) : new_attachment_import_url(@attachment)) + } format.js { render json: {rows: @attachment.rows(4)}, status: :ok } end else diff --git a/app/helpers/mappings_helper.rb b/app/helpers/mappings_helper.rb new file mode 100644 index 0000000..c1f5664 --- /dev/null +++ b/app/helpers/mappings_helper.rb @@ -0,0 +1,5 @@ +module MappingsHelper + def mapping_options + Mapping.by_company(session['company_id']).with_fields.map{|m| [m.title, m.id]} + end +end diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 6295120..94ca25f 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -20,9 +20,9 @@ class Attachment < ActiveRecord::Base after_destroy :delete_file validates :filename, :disk_filename, presence: true - validates :col_sep, :quote_char, presence: true + validates :col_sep, :quote_char, :encoding, presence: true - attr_accessible :col_sep, :quote_char, :uploaded_data + attr_accessible :col_sep, :quote_char, :uploaded_data, :encoding, :mapping_id attr_reader :error_rows # Any upload file gets passed in as uploaded_data attribute @@ -53,8 +53,8 @@ def rows(size = 0) # When parsing data, we expect our file to be saved as valid utf-8 def parsed_data @parsed_data ||= begin - CSV.read(full_filename, col_sep: col_sep, quote_char: quote_char) - rescue CSV::MalformedCSVError => er + CSV.read(full_filename, col_sep: col_sep, quote_char: quote_char, encoding: encoding) + rescue #CSV::MalformedCSVError => er rows = [] #one more attempt. If BOM is present in the file. begin diff --git a/app/models/mapping.rb b/app/models/mapping.rb index a81b969..cd474ac 100644 --- a/app/models/mapping.rb +++ b/app/models/mapping.rb @@ -8,7 +8,12 @@ class Mapping < ActiveRecord::Base default_scope order('mappings.id desc') accepts_nested_attributes_for :mapping_elements - + scope :by_company, lambda{|company_id| where(:company_id => company_id)} + scope :with_fields, joins(:mapping_elements). + select('mappings.id, count(mapping_elements.id) as element_count'). + group('mappings.id'). + having('element_count > 0') + def title I18n.t('mappings.title', count: mapping_elements.count, fields: mapping_elements.collect(&:target).to_sentence) end diff --git a/app/views/attachments/new.html.haml b/app/views/attachments/new.html.haml index 7dcf3e8..14529ff 100644 --- a/app/views/attachments/new.html.haml +++ b/app/views/attachments/new.html.haml @@ -10,13 +10,17 @@ = form_for @attachment, method: 'put' do |f| .clearfix - = label_tag :col_sep, Attachment.human_attribute_name(:col_sep) + = label_tag :col_sep, "#{Attachment.human_attribute_name(:col_sep)} *".html_safe .input = f.text_field :col_sep, class: 'minimi' .clearfix - = label_tag :quote_char, Attachment.human_attribute_name(:quote_char) + = label_tag :quote_char, "#{Attachment.human_attribute_name(:quote_charp)} *".html_safe .input = f.text_field :quote_char, class: 'minimi' + .clearfix + = label_tag :encoding, "#{Attachment.human_attribute_name(:encoding)} *".html_safe + .input + = f.text_field :encoding - if @attachment.new_record? .clearfix %label=t(:'attachments.upload') @@ -29,4 +33,4 @@ .actions = f.submit t('attachments.proceed_to_mapping'), class: "btn primary large" - = link_to t('link.back_to_list'), attachments_path, class: "btn secondary" \ No newline at end of file + = link_to t('link.back_to_list'), attachments_path, class: "btn secondary" diff --git a/app/views/mappings/new.html.haml b/app/views/mappings/new.html.haml index ef4686f..6c1379a 100644 --- a/app/views/mappings/new.html.haml +++ b/app/views/mappings/new.html.haml @@ -1,6 +1,12 @@ %h2 = t('link.step', count: 2, total: 3) = t('mappings.new') + += form_for @attachment do |f| + %p= t('mappings.reuse') + = f.select 'mapping_id', options_for_select(mapping_options) + = f.submit t('mappings.proceed_to_import'), :class => "btn primary large" +%hr = form_for [@attachment, @mapping] do |f| .clearfix @@ -22,4 +28,4 @@ .target{attrs.merge('data-target' => name)}= name .actions = f.submit t('mappings.proceed_to_import'), :class => "btn primary large" - = link_to t('link.back_to_list'), attachments_path, class: "btn secondary" \ No newline at end of file + = link_to t('link.back_to_list'), attachments_path, class: "btn secondary" diff --git a/config/locales/en.yml b/config/locales/en.yml index 9b200f8..055f9e0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -35,7 +35,8 @@ en: new: Upload new CSV file upload: Upload proceed_to_mapping: Proceed to mapping - delete_confirm: "Please note that, this is irreversible process. This will delete associated imports. Are you sure?" + delete_confirm: "Please note that, this is irreversible process. This will delete associated imports. Are you sure?" + csv_import_error: "Try with different 'Column Separator' and/or 'Quote character'." mappings: title: one: "%{count} field: %{fields}" @@ -44,6 +45,7 @@ en: proceed_to_import: Proceed to import source_fields: Source fields target_fields: Target fields + reuse: Select an existing mapping from the list to reuse that or create a new mapping in the below section. dnd_info: "Please drag&drop fields above onto their targets below. Drop multiple fields on a target to join values (space-delimited) e.g for the tag list." imports: title_success: diff --git a/db/migrate/20120213113143_add_encoding_to_attachments.rb b/db/migrate/20120213113143_add_encoding_to_attachments.rb new file mode 100644 index 0000000..f781a29 --- /dev/null +++ b/db/migrate/20120213113143_add_encoding_to_attachments.rb @@ -0,0 +1,5 @@ +class AddEncodingToAttachments < ActiveRecord::Migration + def change + add_column :attachments, :encoding, :string + end +end diff --git a/spec/controllers/attachments_controller_spec.rb b/spec/controllers/attachments_controller_spec.rb index 4685fff..cf6b55f 100644 --- a/spec/controllers/attachments_controller_spec.rb +++ b/spec/controllers/attachments_controller_spec.rb @@ -106,27 +106,27 @@ describe "POST #create" do it "creates new attachment" do lambda { - post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"' + post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"', encoding: 'utf-8' }.should change(Attachment, :count).by(1) end it "reveals new attachment" do - post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"' + post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"', encoding: 'utf-8' assigns[:attachment].should_not be_nil end it "sets attachment user_id" do - post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"' + post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"', encoding: 'utf-8' assigns[:attachment].user_id.should == @user_id end it "sets attachment company_id" do - post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"' + post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"', encoding: 'utf-8' assigns[:attachment].company_id.should == @company_id end it "renders successful json response" do - post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"' + post :create, file: file_upload('test1.csv'), col_sep: ';', quote_char: '"', encoding: 'utf-8' response.content_type.should == "application/json" response.code.should == "200" end @@ -152,11 +152,16 @@ assigns[:attachment].quote_char.should == '^' end - it "redirects to new attachment mapping on html request" do - put :update, id: @authorized_attachment, attachment: {col_sep: ';'} + it "redirects to new attachment mapping on html request if mapping is not set" do + put :update, id: @authorized_attachment, attachment: {col_sep: ';', mapping_id: ''} response.should redirect_to(new_attachment_mapping_url(@authorized_attachment)) end + it "redirects to new attachment import on html request if mapping is set" do + mapping = Factory(:mapping) + put :update, id: @authorized_attachment, attachment: {col_sep: ';', mapping_id: mapping.id} + response.should redirect_to(new_attachment_import_url(@authorized_attachment)) + end it "reneders successful json response on js request" do put :update, id: @authorized_attachment, attachment: {col_sep: ';'}, format: 'js' response.code.should == "200" diff --git a/spec/factories.rb b/spec/factories.rb index a042805..6f0e140 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -3,6 +3,7 @@ uploaded_data { file_upload('test1.csv') } col_sep ';' quote_char '"' + encoding 'utf-8' association :mapping end diff --git a/spec/models/attachment_spec.rb b/spec/models/attachment_spec.rb index 374732e..db49605 100644 --- a/spec/models/attachment_spec.rb +++ b/spec/models/attachment_spec.rb @@ -3,7 +3,15 @@ describe Attachment do it { should belong_to(:mapping) } it { should have_many(:imports).dependent(:destroy) } - + + ['filename', 'col_sep', 'quote_char', 'encoding', 'disk_filename'].each do |attribute| + it { should validate_presence_of(attribute)} + end + + [:col_sep, :quote_char, :uploaded_data, :encoding, :mapping_id].each do |attribute| + it { should allow_mass_assignment_of(attribute) } + end + before :each do @attachment = Factory(:attachment) end diff --git a/spec/models/mapping_spec.rb b/spec/models/mapping_spec.rb index de00a9a..97c4e8b 100644 --- a/spec/models/mapping_spec.rb +++ b/spec/models/mapping_spec.rb @@ -1,6 +1,41 @@ require 'spec_helper' describe Mapping do + before :each do + @mapping = Factory(:mapping, :company_id => 'a company') + Factory(:mapping_element, mapping: @mapping) + Factory(:gender_mapping_element, mapping: @mapping) + Factory(:birthday_mapping_element, mapping: @mapping) + end + it { should have_many(:mapping_elements).dependent(:destroy) } it { should have_many(:attachments).dependent(:nullify) } + + describe ".by_company" do + it "includes mappings belongs to company" do + Mapping.by_company('a company').should include(@mapping) + end + + it "excludes mappings belongs to other company" do + another_mapping = Factory(:mapping, :company_id => 'another company') + Mapping.by_company('a company').should_not include(another_mapping) + end + end + + describe ".with_fields" do + it "includes mappings with more than one mapping element defined" do + Mapping.with_fields.should include(@mapping) + end + + it "excludes mappings belongs to other company" do + another_mapping = Factory(:mapping, :company_id => 'another company') + Mapping.with_fields.should_not include(another_mapping) + end + end + + describe '#title' do + it 'should return mapping details' do + @mapping.title.should == '3 fields: organization, gender, and birthday' + end + end end