Implemented issue #2064 - Add delete existing records to CSV import feature.
This commit is contained in:
parent
4b8e615640
commit
8af6e2b9eb
15 changed files with 198 additions and 24 deletions
|
@ -663,6 +663,7 @@ class App.ControllerModal extends App.Controller
|
|||
large: false
|
||||
small: false
|
||||
head: '?'
|
||||
autoFocusOnFirstInput: true
|
||||
container: null
|
||||
buttonClass: 'btn--success'
|
||||
centerButtons: []
|
||||
|
@ -812,6 +813,7 @@ class App.ControllerModal extends App.Controller
|
|||
@onShown(e)
|
||||
|
||||
onShown: (e) =>
|
||||
if @autoFocusOnFirstInput
|
||||
@$('input:not([disabled]):not([type="hidden"]):not(".btn"), textarea').first().focus()
|
||||
@initalFormParams = @formParams()
|
||||
|
||||
|
|
|
@ -1285,6 +1285,7 @@ class App.Import extends App.ControllerModal
|
|||
buttonClose: true
|
||||
buttonCancel: true
|
||||
buttonSubmit: 'Import'
|
||||
autoFocusOnFirstInput: false
|
||||
head: 'Import'
|
||||
large: true
|
||||
templateDirectory: 'generic/object_import'
|
||||
|
@ -1296,6 +1297,7 @@ class App.Import extends App.ControllerModal
|
|||
content = $(App.view("#{@templateDirectory}/index")(
|
||||
head: 'Import'
|
||||
import_example_url: "#{@baseUrl}/import_example"
|
||||
deleteOption: @deleteOption
|
||||
))
|
||||
|
||||
# check if data is processing...
|
||||
|
@ -1342,6 +1344,7 @@ class App.ImportTryResult extends App.ControllerModal
|
|||
buttonClose: true
|
||||
buttonCancel: true
|
||||
buttonSubmit: 'Yes, start real import.'
|
||||
autoFocusOnFirstInput: false
|
||||
head: 'Import'
|
||||
large: true
|
||||
templateDirectory: 'generic/object_import/'
|
||||
|
@ -1386,6 +1389,7 @@ class App.ImportResult extends App.ControllerModal
|
|||
buttonClose: true
|
||||
buttonCancel: true
|
||||
buttonSubmit: 'Close'
|
||||
autoFocusOnFirstInput: false
|
||||
head: 'Import'
|
||||
large: true
|
||||
templateDirectory: 'generic/object_import/'
|
||||
|
|
|
@ -12,6 +12,7 @@ class Index extends App.ControllerSubContent
|
|||
new App.Import(
|
||||
baseUrl: '/api/v1/text_modules'
|
||||
container: @el.closest('.content')
|
||||
deleteOption: true
|
||||
)
|
||||
pageData:
|
||||
home: 'text_modules'
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
<h2><input checked="checked" disabled="disabled" type="checkbox" name="update" value="true"> <%- @T('Update existing records') %></h2>
|
||||
<%- @T('Update existing records with the attributes specified in the import data.') %>
|
||||
|
||||
<!--
|
||||
<h2><input checked="" type="checkbox" name="delete" value="true"> <%- @T('Delete records') %></h2>
|
||||
<%- @T('Delete all existigs records first.') %>
|
||||
-->
|
||||
<% if @deleteOption is true: %>
|
||||
<h2><input type="checkbox" name="delete" value="true"> <%- @T('Delete records') %></h2>
|
||||
<%- @T('Delete all existing records first.') %>
|
||||
<% end %>
|
||||
|
||||
<h2><%- @T('Select CSV file') %></h2>
|
||||
<input name="file" type="file">
|
||||
|
|
|
@ -354,6 +354,7 @@ curl http://localhost/api/v1/organization/{id} -v -u #{login}:#{password} -H "Co
|
|||
col_sep: params[:col_sep] || ',',
|
||||
},
|
||||
try: params[:try],
|
||||
delete: params[:delete],
|
||||
)
|
||||
render json: result, status: :ok
|
||||
end
|
||||
|
|
|
@ -193,6 +193,7 @@ curl http://localhost/api/v1/text_modules.json -v -u #{login}:#{password} -H "Co
|
|||
col_sep: params[:col_sep] || ',',
|
||||
},
|
||||
try: params[:try],
|
||||
delete: params[:delete],
|
||||
)
|
||||
render json: result, status: :ok
|
||||
end
|
||||
|
|
|
@ -1107,6 +1107,7 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content
|
|||
col_sep: params[:col_sep] || ',',
|
||||
},
|
||||
try: params[:try],
|
||||
delete: params[:delete],
|
||||
)
|
||||
render json: result, status: :ok
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ module CanCsvImport
|
|||
col_sep: ',',
|
||||
},
|
||||
try: true,
|
||||
delete: false,
|
||||
)
|
||||
|
||||
result = Model.csv_import(
|
||||
|
@ -24,6 +25,7 @@ module CanCsvImport
|
|||
col_sep: ',',
|
||||
},
|
||||
try: true,
|
||||
delete: false,
|
||||
)
|
||||
|
||||
result = TextModule.csv_import(
|
||||
|
@ -32,6 +34,7 @@ module CanCsvImport
|
|||
col_sep: ',',
|
||||
},
|
||||
try: false,
|
||||
delete: false,
|
||||
)
|
||||
|
||||
returns
|
||||
|
@ -45,7 +48,25 @@ returns
|
|||
=end
|
||||
|
||||
def csv_import(data)
|
||||
try = true
|
||||
if data[:try] != 'true' && data[:try] != true
|
||||
try = false
|
||||
end
|
||||
delete = false
|
||||
if data[:delete] == true || data[:delete] == 'true'
|
||||
delete = true
|
||||
end
|
||||
|
||||
errors = []
|
||||
if delete == true && @csv_delete_possible != true
|
||||
errors.push "Delete is not possible for #{new.class}."
|
||||
result = {
|
||||
errors: errors,
|
||||
try: try,
|
||||
result: 'failed',
|
||||
}
|
||||
return result
|
||||
end
|
||||
|
||||
if data[:file].present?
|
||||
raise Exceptions::UnprocessableEntity, "No such file '#{data[:file]}'" if !File.exist?(data[:file])
|
||||
|
@ -60,7 +81,7 @@ returns
|
|||
errors.push "Unable to parse empty file/string for #{new.class}."
|
||||
result = {
|
||||
errors: errors,
|
||||
try: data[:try],
|
||||
try: try,
|
||||
result: 'failed',
|
||||
}
|
||||
return result
|
||||
|
@ -72,7 +93,7 @@ returns
|
|||
errors.push "Unable to parse file/string without header for #{new.class}."
|
||||
result = {
|
||||
errors: errors,
|
||||
try: data[:try],
|
||||
try: try,
|
||||
result: 'failed',
|
||||
}
|
||||
return result
|
||||
|
@ -89,7 +110,7 @@ returns
|
|||
errors.push "No records found in file/string for #{new.class}."
|
||||
result = {
|
||||
errors: errors,
|
||||
try: data[:try],
|
||||
try: try,
|
||||
result: 'failed',
|
||||
}
|
||||
return result
|
||||
|
@ -127,13 +148,22 @@ returns
|
|||
payload.push attributes
|
||||
end
|
||||
|
||||
# create or update records
|
||||
csv_object_ids_ignored = @csv_object_ids_ignored || []
|
||||
records = []
|
||||
stats = {
|
||||
created: 0,
|
||||
updated: 0,
|
||||
}
|
||||
|
||||
# delete
|
||||
if delete == true
|
||||
stats[:deleted] = self.count
|
||||
if try == false
|
||||
destroy_all
|
||||
end
|
||||
end
|
||||
|
||||
# create or update records
|
||||
csv_object_ids_ignored = @csv_object_ids_ignored || []
|
||||
records = []
|
||||
line_count = 0
|
||||
payload.each do |attributes|
|
||||
line_count += 1
|
||||
|
@ -166,7 +196,7 @@ returns
|
|||
# create object
|
||||
Transaction.execute(disable_notification: true, reset_user_id: true) do
|
||||
UserInfo.current_user_id = clean_params[:updated_by_id] || clean_params[:created_by_id]
|
||||
if !record
|
||||
if !record || delete == true
|
||||
stats[:created] += 1
|
||||
begin
|
||||
csv_verify_attributes(clean_params)
|
||||
|
@ -177,7 +207,7 @@ returns
|
|||
clean_params[:updated_by_id] = 1
|
||||
end
|
||||
record = new(clean_params)
|
||||
next if data[:try] == 'true' || data[:try] == true
|
||||
next if try == true
|
||||
record.associations_from_param(attributes)
|
||||
record.save!
|
||||
rescue => e
|
||||
|
@ -186,7 +216,7 @@ returns
|
|||
end
|
||||
else
|
||||
stats[:updated] += 1
|
||||
next if data[:try] == 'true' || data[:try] == true
|
||||
next if try == true
|
||||
begin
|
||||
csv_verify_attributes(clean_params)
|
||||
clean_params = param_cleanup(clean_params)
|
||||
|
@ -218,7 +248,7 @@ returns
|
|||
stats: stats,
|
||||
records: records,
|
||||
errors: errors,
|
||||
try: data[:try],
|
||||
try: try,
|
||||
result: result,
|
||||
}
|
||||
|
||||
|
@ -371,5 +401,20 @@ end
|
|||
@csv_attributes_ignored = attributes
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
serve methode to define if delete option is possible or not
|
||||
|
||||
class Model < ApplicationModel
|
||||
include CanCsvImport
|
||||
csv_delete_possible true
|
||||
|
||||
end
|
||||
|
||||
=end
|
||||
|
||||
def csv_delete_possible(value)
|
||||
@csv_delete_possible = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,6 +13,8 @@ class TextModule < ApplicationModel
|
|||
|
||||
sanitized_html :content
|
||||
|
||||
csv_delete_possible true
|
||||
|
||||
=begin
|
||||
|
||||
load text modules from online
|
||||
|
|
|
@ -538,7 +538,7 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_equal('true', result['try'])
|
||||
assert_equal(true, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('failed', result['result'])
|
||||
assert_equal(2, result['errors'].count)
|
||||
|
@ -553,7 +553,7 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_equal('true', result['try'])
|
||||
assert_equal(true, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('success', result['result'])
|
||||
|
||||
|
@ -568,7 +568,7 @@ class OrganizationControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_nil(result['try'])
|
||||
assert_equal(false, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('success', result['result'])
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_equal('true', result['try'])
|
||||
assert_equal(true, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('failed', result['result'])
|
||||
assert_equal(2, result['errors'].count)
|
||||
|
@ -123,7 +123,7 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_equal('true', result['try'])
|
||||
assert_equal(true, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('success', result['result'])
|
||||
|
||||
|
@ -138,7 +138,7 @@ class TextModuleControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_nil(result['try'])
|
||||
assert_equal(false, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('success', result['result'])
|
||||
|
||||
|
|
|
@ -985,7 +985,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_equal('true', result['try'])
|
||||
assert_equal(true, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('failed', result['result'])
|
||||
assert_equal(2, result['errors'].count)
|
||||
|
@ -1000,7 +1000,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_equal('true', result['try'])
|
||||
assert_equal(true, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('success', result['result'])
|
||||
|
||||
|
@ -1015,7 +1015,7 @@ class UserControllerTest < ActionDispatch::IntegrationTest
|
|||
result = JSON.parse(@response.body)
|
||||
assert_equal(Hash, result.class)
|
||||
|
||||
assert_nil(result['try'])
|
||||
assert_equal(false, result['try'])
|
||||
assert_equal(2, result['records'].count)
|
||||
assert_equal('success', result['result'])
|
||||
|
||||
|
|
|
@ -234,4 +234,20 @@ class OrganizationCsvImportTest < ActiveSupport::TestCase
|
|||
assert_nil(Organization.find_by(name: 'organization-invalid-import2'))
|
||||
end
|
||||
|
||||
test 'simple import with delete' do
|
||||
csv_string = "id;name;shared;domain;domain_assignment;active;note\n;org-simple-import1;true;org-simple-import1.example.com;false;true;some note1\n;org-simple-import2;true;org-simple-import2.example.com;false;false;some note2\n"
|
||||
result = Organization.csv_import(
|
||||
string: csv_string,
|
||||
parse_params: {
|
||||
col_sep: ';',
|
||||
},
|
||||
try: true,
|
||||
delete: true,
|
||||
)
|
||||
|
||||
assert_equal(true, result[:try])
|
||||
assert_equal('failed', result[:result])
|
||||
assert_equal('Delete is not possible for Organization.', result[:errors][0])
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -51,6 +51,13 @@ class TextModuleCsvImportTest < ActiveSupport::TestCase
|
|||
end
|
||||
|
||||
test 'simple import' do
|
||||
TextModule.create!(
|
||||
name: 'nsome name1',
|
||||
content: 'nsome name1',
|
||||
active: true,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
csv_string = "name;keywords;content;note;active;\nsome name1;keyword1;\"some\ncontent1\";-;\nsome name2;keyword2;some content<br>test123\n"
|
||||
result = TextModule.csv_import(
|
||||
|
@ -97,4 +104,82 @@ class TextModuleCsvImportTest < ActiveSupport::TestCase
|
|||
text_module2.destroy!
|
||||
end
|
||||
|
||||
test 'simple import with delete' do
|
||||
assert_equal(0, TextModule.count)
|
||||
|
||||
TextModule.create!(
|
||||
name: 'some name1',
|
||||
content: 'some name1',
|
||||
active: true,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
TextModule.create!(
|
||||
name: 'name should be deleted 2',
|
||||
content: 'name should be deleted 1',
|
||||
active: true,
|
||||
updated_by_id: 1,
|
||||
created_by_id: 1,
|
||||
)
|
||||
|
||||
csv_string = "name;keywords;content;note;active;\nsome name1;keyword1;\"some\ncontent1\";-;\nsome name2;keyword2;some content<br>test123\n"
|
||||
result = TextModule.csv_import(
|
||||
string: csv_string,
|
||||
parse_params: {
|
||||
col_sep: ';',
|
||||
},
|
||||
try: true,
|
||||
delete: true,
|
||||
)
|
||||
|
||||
assert_equal(true, result[:try])
|
||||
assert(result[:stats])
|
||||
assert_equal(2, result[:stats][:created])
|
||||
assert_equal(0, result[:stats][:updated])
|
||||
assert_equal(2, result[:stats][:deleted])
|
||||
|
||||
assert_equal(2, result[:records].count)
|
||||
assert_equal('success', result[:result])
|
||||
|
||||
assert(TextModule.find_by(name: 'some name1'))
|
||||
assert_nil(TextModule.find_by(name: 'some name2'))
|
||||
|
||||
result = TextModule.csv_import(
|
||||
string: csv_string,
|
||||
parse_params: {
|
||||
col_sep: ';',
|
||||
},
|
||||
try: false,
|
||||
delete: true,
|
||||
)
|
||||
|
||||
assert_equal(false, result[:try])
|
||||
assert(result[:stats])
|
||||
assert_equal(2, result[:stats][:created])
|
||||
assert_equal(0, result[:stats][:updated])
|
||||
assert_equal(2, result[:stats][:deleted])
|
||||
assert_equal(2, result[:records].count)
|
||||
assert_equal('success', result[:result])
|
||||
|
||||
assert_equal(2, TextModule.count)
|
||||
|
||||
text_module1 = TextModule.find_by(name: 'some name1')
|
||||
assert(text_module1)
|
||||
assert_equal(text_module1.name, 'some name1')
|
||||
assert_equal(text_module1.keywords, 'keyword1')
|
||||
assert_equal(text_module1.content, 'some<br>content1')
|
||||
assert_equal(text_module1.active, true)
|
||||
text_module2 = TextModule.find_by(name: 'some name2')
|
||||
assert(text_module2)
|
||||
assert_equal(text_module2.name, 'some name2')
|
||||
assert_equal(text_module2.keywords, 'keyword2')
|
||||
assert_equal(text_module2.content, 'some content<br>test123')
|
||||
assert_equal(text_module2.active, true)
|
||||
|
||||
assert_nil(TextModule.find_by(name: 'name should be deleted 2'))
|
||||
|
||||
text_module1.destroy!
|
||||
text_module2.destroy!
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -441,4 +441,20 @@ class UserCsvImportTest < ActiveSupport::TestCase
|
|||
orgaization2.destroy!
|
||||
end
|
||||
|
||||
test 'simple import with delete' do
|
||||
csv_string = "login;firstname;lastname;email\nuser-simple-import-fixed1;firstname-simple-import-fixed1;lastname-simple-import-fixed1;user-simple-import-fixed1@example.com\nuser-simple-import-fixed2;firstname-simple-import-fixed2;lastname-simple-import-fixed2;user-simple-import-fixed2@example.com\n"
|
||||
result = User.csv_import(
|
||||
string: csv_string,
|
||||
parse_params: {
|
||||
col_sep: ';',
|
||||
},
|
||||
try: true,
|
||||
delete: true,
|
||||
)
|
||||
|
||||
assert_equal(true, result[:try])
|
||||
assert_equal('failed', result[:result])
|
||||
assert_equal('Delete is not possible for User.', result[:errors][0])
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue