Fix importing files with empty header cells and error messages improvements [SCI-1484]

This commit is contained in:
Oleksii Kriuchykhin 2017-07-18 14:54:35 +02:00
parent 036ee390ed
commit ece27b2652
9 changed files with 65 additions and 54 deletions

View file

@ -46,7 +46,7 @@ gem 'ajax-datatables-rails', '~> 0.3.1'
gem 'commit_param_routing' # Enables different submit actions in the same form to route to different actions in controller gem 'commit_param_routing' # Enables different submit actions in the same form to route to different actions in controller
gem 'kaminari' gem 'kaminari'
gem "i18n-js", ">= 3.0.0.rc11" # Localization in javascript files gem "i18n-js", ">= 3.0.0.rc11" # Localization in javascript files
gem 'roo', '~> 2.1.0' # Spreadsheet parser gem 'roo', '~> 2.7.1' # Spreadsheet parser
gem 'wicked_pdf' gem 'wicked_pdf'
gem 'silencer' # Silence certain Rails logs gem 'silencer' # Silence certain Rails logs
gem 'wkhtmltopdf-heroku' gem 'wkhtmltopdf-heroku'

View file

@ -190,9 +190,8 @@ GEM
jquery-rails jquery-rails
rails (>= 3.2.0) rails (>= 3.2.0)
newrelic_rpm (4.0.0.332) newrelic_rpm (4.0.0.332)
nokogiri (1.6.8) nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0) mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
nokogumbo (1.4.10) nokogumbo (1.4.10)
nokogiri nokogiri
oj (2.17.4) oj (2.17.4)
@ -206,7 +205,6 @@ GEM
parser (2.4.0.0) parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
pkg-config (1.1.7)
polyglot (0.3.5) polyglot (0.3.5)
powerpack (0.1.1) powerpack (0.1.1)
puma (2.15.3) puma (2.15.3)
@ -257,7 +255,7 @@ GEM
algorithms (~> 0.6.1) algorithms (~> 0.6.1)
stream (~> 0.5.0) stream (~> 0.5.0)
rkelly-remix (0.0.7) rkelly-remix (0.0.7)
roo (2.1.1) roo (2.7.1)
nokogiri (~> 1) nokogiri (~> 1)
rubyzip (~> 1.1, < 2.0.0) rubyzip (~> 1.1, < 2.0.0)
rubocop (0.48.1) rubocop (0.48.1)
@ -268,7 +266,7 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-graphviz (1.2.2) ruby-graphviz (1.2.2)
ruby-progressbar (1.8.1) ruby-progressbar (1.8.1)
rubyzip (1.1.7) rubyzip (1.2.1)
sanitize (4.4.0) sanitize (4.4.0)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
@ -390,7 +388,7 @@ DEPENDENCIES
recaptcha recaptcha
remotipart (~> 1.2) remotipart (~> 1.2)
rgl rgl
roo (~> 2.1.0) roo (~> 2.7.1)
rubocop rubocop
ruby-graphviz (~> 1.2) ruby-graphviz (~> 1.2)
rubyzip rubyzip

View file

@ -238,8 +238,9 @@ class RepositoriesController < ApplicationController
number_of_rows: status[:nr_of_added]) number_of_rows: status[:nr_of_added])
render json: {}, status: :ok render json: {}, status: :ok
else else
flash[:alert] = t('repositories.import_records.error_flash', flash[:alert] =
message: status[:errors]) t('repositories.import_records.partial_success_flash',
nr: status[:nr_of_added], total_nr: status[:total_nr])
render json: {}, status: :unprocessable_entity render json: {}, status: :unprocessable_entity
end end
else else

View file

@ -36,8 +36,7 @@ class TeamsController < ApplicationController
# Get data (it will trigger any errors as well) # Get data (it will trigger any errors as well)
@header = sheet.row(1) @header = sheet.row(1)
@rows = []; @columns = sheet.row(2)
@rows << Hash[[@header, sheet.row(2)].transpose]
# Fill in fields for dropdown # Fill in fields for dropdown
@available_fields = @team.get_available_sample_fields @available_fields = @team.get_available_sample_fields

View file

@ -109,9 +109,10 @@ class Repository < ActiveRecord::Base
# Imports records # Imports records
def import_records(sheet, mappings, user) def import_records(sheet, mappings, user)
errors = [] errors = false
custom_fields = [] custom_fields = []
name_index = -1 name_index = -1
total_nr = 0
nr_of_added = 0 nr_of_added = 0
mappings.each.with_index do |(_k, value), index| mappings.each.with_index do |(_k, value), index|
@ -127,13 +128,17 @@ class Repository < ActiveRecord::Base
# Now we can iterate through record data and save stuff into db # Now we can iterate through record data and save stuff into db
(2..sheet.last_row).each do |i| (2..sheet.last_row).each do |i|
error = [] total_nr += 1
cell_error = false
record_row = RepositoryRow.new(name: sheet.row(i)[name_index], record_row = RepositoryRow.new(name: sheet.row(i)[name_index],
repository: self, repository: self,
created_by: user, created_by: user,
last_modified_by: user) last_modified_by: user)
next unless record_row.valid? unless record_row.valid?
errors = true
next
end
sheet.row(i).each.with_index do |value, index| sheet.row(i).each.with_index do |value, index|
if custom_fields[index] && value if custom_fields[index] && value
rep_column = RepositoryTextValue.new( rep_column = RepositoryTextValue.new(
@ -145,10 +150,11 @@ class Repository < ActiveRecord::Base
repository_column: custom_fields[index] repository_column: custom_fields[index]
} }
) )
error << rep_column.errors.messages unless rep_column.save cell_error = true unless rep_column.save
end end
end end
if error.any? if cell_error
errors = true
record_row.destroy record_row.destroy
else else
nr_of_added += 1 nr_of_added += 1
@ -156,10 +162,12 @@ class Repository < ActiveRecord::Base
end end
end end
if errors.count > 0 if errors
return { status: :error, errors: errors, nr_of_added: nr_of_added } return { status: :error,
nr_of_added: nr_of_added,
total_nr: total_nr }
end end
{ status: :ok, nr_of_added: nr_of_added } { status: :ok, nr_of_added: nr_of_added, total_nr: total_nr }
end end
private private

View file

@ -11,15 +11,14 @@ module ImportRepository
def data def data
# Get data (it will trigger any errors as well) # Get data (it will trigger any errors as well)
header = @sheet.row(1) header = @sheet.row(1)
rows = [] columns = @sheet.row(2)
rows << Hash[[header, @sheet.row(2)].transpose]
# Fill in fields for dropdown # Fill in fields for dropdown
@repository.available_repository_fields.transform_values! do |name| @repository.available_repository_fields.transform_values! do |name|
truncate(name, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) truncate(name, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN)
end end
@temp_file = TempFile.create(session_id: @session.id, file: @file) @temp_file = TempFile.create(session_id: @session.id, file: @file)
Data.new(header, Data.new(header,
rows, columns,
@repository.available_repository_fields, @repository.available_repository_fields,
@repository, @repository,
@temp_file) @temp_file)
@ -47,7 +46,7 @@ module ImportRepository
end end
Data = Struct.new( Data = Struct.new(
:header, :rows, :available_fields, :repository, :temp_file :header, :columns, :available_fields, :repository, :temp_file
) )
end end
end end

View file

@ -30,29 +30,31 @@
include_blank: t('teams.parse_sheet.do_not_include_column'), include_blank: t('teams.parse_sheet.do_not_include_column'),
hide_label: true) %> hide_label: true) %>
<br /> <br />
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %> <% if th.nil? %>
<div class="modal-tooltip"> <i><%= t('repositories.import_records.no_header_name') %></i>
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
</div>
<% else %> <% else %>
<%= th %> <% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
<div class="modal-tooltip">
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
</div>
<% else %>
<%= th %>
<% end %>
<% end %> <% end %>
</th> </th>
<% end %> <% end %>
</thead> </thead>
<tbody> <tbody>
<% @import_data.rows.each do |row| %> <tr>
<tr> <td>
<p><%= t('teams.parse_sheet.example_value') %></p>
</td>
<% @import_data.columns.each do |td| %>
<td> <td>
<p><%= t('teams.parse_sheet.example_value') %></p> <%= td %>
</td> </td>
<% row.each do |td| %> <% end %>
<td> </tr>
<%= td[1] %>
</td>
<% end %>
</tr>
<% end %>
</tbody> </tbody>
</table> </table>
</div> </div>

View file

@ -25,29 +25,31 @@
include_blank: t('teams.parse_sheet.do_not_include_column'), include_blank: t('teams.parse_sheet.do_not_include_column'),
hide_label: true) %> hide_label: true) %>
<br /> <br />
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %> <% if th.nil? %>
<div class="modal-tooltip"> <i><%= t('samples.modal_import.no_header_name') %></i>
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
</div>
<% else %> <% else %>
<%= th %> <% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
<div class="modal-tooltip">
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
</div>
<% else %>
<%= th %>
<% end %>
<% end %> <% end %>
</th> </th>
<% end %> <% end %>
</thead> </thead>
<tbody> <tbody>
<% @rows.each do |row| %> <tr>
<tr> <td>
<p><%= t('teams.parse_sheet.example_value') %></p>
</td>
<% @columns.each do |td| %>
<td> <td>
<p><%= t('teams.parse_sheet.example_value') %></p> <%= td %>
</td> </td>
<% row.each do |td| %> <% end %>
<td> </tr>
<%= td[1] %>
</td>
<% end %>
</tr>
<% end %>
</tbody> </tbody>
</table> </table>
</div> </div>

View file

@ -911,8 +911,9 @@ en:
add_new_record: "Add new item" add_new_record: "Add new item"
import_records: import_records:
import: 'Import' import: 'Import'
no_header_name: 'No column name'
success_flash: "%{number_of_rows} new item(s) successfully imported." success_flash: "%{number_of_rows} new item(s) successfully imported."
error_flash: "Something went wrong: %{message}" partial_success_flash: "%{nr} of %{total_nr} successfully imported. Other rows contained errors."
error_message: error_message:
temp_file_not_found: "This file could not be found. Your session might expire." temp_file_not_found: "This file could not be found. Your session might expire."
session_expired: "Your session expired. Please try again." session_expired: "Your session expired. Please try again."
@ -1009,6 +1010,7 @@ en:
modal_import: modal_import:
title: "Import samples" title: "Import samples"
notice: "You may upload .csv file (comma separated) or tab separated file (.txt or .tdv) or Excel file (.xls, .xlsx). First row should include header names, followed by rows with sample data." notice: "You may upload .csv file (comma separated) or tab separated file (.txt or .tdv) or Excel file (.xls, .xlsx). First row should include header names, followed by rows with sample data."
no_header_name: 'No column name'
upload: "Upload file" upload: "Upload file"
modal_delete: modal_delete:
title: "Delete samples" title: "Delete samples"