split the import service

This commit is contained in:
zmagod 2017-06-19 14:05:37 +02:00
parent 2d3b5f4308
commit 70fa681519
10 changed files with 542 additions and 446 deletions

View file

@ -0,0 +1,53 @@
(function(global) {
'use strict';
global.repositoryRecordsImporter = function() {
var previousIndex, disabledOptions, loadingSamples;
loadingSamples = false;
$('select').focus(function() {
previousIndex = $(this)[0].selectedIndex;
}).change(function() {
var currSelect = $(this);
var currIndex = $(currSelect)[0].selectedIndex;
$('select').each(function() {
var el = $(this);
if (currSelect !== el && currIndex > 0) {
el.children().eq(currIndex).attr('disabled', 'disabled');
}
el.children().eq(previousIndex).removeAttr('disabled');
});
previousIndex = currIndex;
});
// Create import samples ajax
$('form#form-import').submit(function(e) {
// Check if we already uploading samples
if (loadingSamples) {
return false;
}
disabledOptions = $("option[disabled='disabled']");
disabledOptions.removeAttr('disabled');
loadingSamples = true;
animateSpinner();
}).on('ajax:success', function(ev, data, status) {
// Simply reload page to show flash and updated samples list
loadingSamples = false;
location.reload();
}).on('ajax:error', function(ev, data, status) {
loadingSamples = false;
if (_.isUndefined(data.responseJSON.html)) {
// Simply reload page to show flash
location.reload();
} else {
// Re-disable options
disabledOptions.attr('disabled', 'disabled');
// Populate the errors container
$('#import-errors-container').html(data.responseJSON.html);
}
});
}
})(window);

View file

@ -1,6 +1,36 @@
//= require repositories/import/records_importer.js
(function() {
'use strict';
function showNewRepository() {
$('#create-repo-modal').on('ajax:success', function(data) {
var location = data.url;
window.location.replace(location);
});
}
function initCreateRepository() {
$('.create-repository').off().on('click', function(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
$.ajax({
url: $(this).attr('href'),
type: 'GET',
dataType: 'json',
success: function(data) {
$(data.html).appendTo('body').promise().done(function() {
$('#create-repo-modal').modal('show');
});
},
error: function() {
location.reload();
}
})
});
}
function loadRepositoryTab() {
var param, pane;
$('#repository-tabs a').on("click", function(e) {
@ -8,7 +38,6 @@
e.stopPropagation();
e.stopImmediatePropagation();
pane = $(this);
$.ajax({
url: pane.attr('data-url'),
type: 'GET',
@ -16,7 +45,9 @@
success: function(data) {
var tabBody = $(pane.context.hash).find('.tab-content-body');
tabBody.html(data.html);
pane.tab('show');
pane.tab('show').promise().done(function() {
initImportRecordsModal();
});
},
error: function (error) {
// TODO
@ -35,25 +66,37 @@
}
// clean tab content
$('a[data-toggle="tab"]').on('hide.bs.tab', function (e) {
$('a[data-toggle="tab"]').on('hide.bs.tab', function(e) {
$(".tab-content-body").html("");
})
}
function showParsedRecords() {
$('#form-records-file').bind('ajax:success', function(evt, data, status, xhr) {
debugger;
function initImportRecordsModal() {
$('#importRecordsButton').off().on('click', function() {
$('#modal-import-records').modal('show');
initParseRecordsModal();
});
}
function initParseRecordsModal() {
$('#form-records-file').on('ajax:success', function(ev, data) {
$('#modal-import-records').modal('hide');
$(data.html).appendTo('body').promise().done(function() {
$('#parse-records_modal').modal('show');
repositoryRecordsImporter();
});
});
};
$('.delete-repo-option').initializeModal('#delete-repo-modal');
$('.rename-repo-option').initializeModal('#rename-repo-modal');
$('.copy-repo-option').initializeModal('#copy-repo-modal');
$('.create-repository').initializeModal('#create-repo-modal');
// $('.create-repository').initializeModal('#create-repo-modal');
$(document).ready(function() {
loadRepositoryTab();
showParsedRecords();
// showParsedRecords();
initCreateRepository();
});
})();

View file

@ -1,12 +1,14 @@
class RepositoriesController < ApplicationController
before_action :load_vars, except: [:repository_table_index, :parse_sheet]
before_action :load_vars,
except: %i(repository_table_index parse_sheet import_records)
before_action :check_team, only: %i(parse_sheet import_records)
before_action :check_view_all_permissions, only: :index
before_action :check_edit_and_destroy_permissions, only:
%(destroy destroy_modal rename_modal update)
before_action :check_copy_permissions, only:
%(copy_modal copy)
before_action :check_create_permissions, only:
%(create_new_modal create)
before_action :check_edit_and_destroy_permissions,
only: %i(destroy destroy_modal rename_modal update)
before_action :check_copy_permissions,
only: %i(copy_modal copy)
before_action :check_create_permissions,
only: %i(create_new_modal create)
def index
render('repositories/index')
@ -186,25 +188,27 @@ class RepositoriesController < ApplicationController
end
def parse_sheet
render_404 unless params[:team_id].to_i == current_team.id
repository = current_team.repositories.find_by_id(params[:id])
imported_file = ::ImportRepository.new(file: params[:file],
repository: repository,
session: session)
parsed_file = ImportRepository::ParseRepository.new(
file: params[:file],
repository: repository,
session: session
)
respond_to do |format|
unless params[:file]
repository_response(t("teams.parse_sheet.errors.no_file_selected"))
return
end
begin
if imported_file.too_large?
if parsed_file.too_large?
repository_response(t('general.file.size_exceeded',
file_size: Constants::FILE_MAX_SIZE_MB))
else
flash[:notice] = t('teams.parse_sheet.errors.empty_file')
redirect_to back and return if imported_file.empty?
@import_data = imported_file.data
if imported_file.generated_temp_file?
redirect_to back and return if parsed_file.empty?
@import_data = parsed_file.data
if parsed_file.generated_temp_file?
format.json do
render json: {
html: render_to_string(
@ -225,7 +229,16 @@ class RepositoriesController < ApplicationController
end
end
def import_repository
def import_records
# byebug
import_records = ImportRepository::ImportRecords.new(
temp_file: TempFile.find_by_id(params[:file_id]),
repository: current_team.repositories.find_by_id(params[:id]),
mappings: params[:mappings],
session: session,
user: current_user
)
import_records.import!
end
private
@ -236,6 +249,10 @@ class RepositoriesController < ApplicationController
@repositories = @team.repositories.order(created_at: :asc)
end
def check_team
render_404 unless params[:team_id].to_i == current_team.id
end
def check_view_all_permissions
render_403 unless can_view_team_repositories(@team)
end

View file

@ -64,8 +64,113 @@ class Repository < ActiveRecord::Base
new_repo
end
# Imports records
def import_records(sheet, mappings, user)
errors = []
custom_fields = []
name_index = -1
header = sheet.row(1)
generate_new_columns(header)
mappings.each.with_index do |(k, v), i|
if v == '-1'
# Fill blank space, so our indices stay the same
custom_fields << nil
name_index = i
else
cf = repository_columns.find_by_name(header[i])
custom_fields << cf
end
end
# byebug
# Now we can iterate through sample data and save stuff into db
(2..sheet.last_row).each do |i|
byebug
error = []
total_nr += 1
byebug
record_row = RepositoryRow.new(name: sheet.row(i)[name_index],
repository: self,
created_by: user,
last_modified_by: user)
if record.save
sheet.row(i).each.with_index do |value, index|
# We need to have sample saved before messing with custom fields (they
# need sample id)
if custom_fields[index]
# we're working with CustomField
rep_column = RepositoryTextValue.new(
data: value,
created_by:
last_modified_by:
repository_cell_attributes: {
repository_row: record,
repository_column: column
}
)
if scf.save
error << scf.errors.messages
end
else
# This custom_field does not exist
error << {"#{mappings[index]}": "Does not exists"}
end
end
end
end
#
# params[:repository_cells].each do |key, value|
# column = @repository.repository_columns.detect do |c|
# c.id == key.to_i
# end
# cell_value = RepositoryTextValue.new(
# data: value,
# created_by: current_user,
# last_modified_by: current_user,
# repository_cell_attributes: {
# repository_row: record,
# repository_column: column
# }
# )
# unless cell_value.save
# errors[:repository_cells] << {
# "#{cell.repository_column.id}": cell_value.errors.messages
# }
# raise ActiveRecord::RecordInvalid
# end
# record_annotation_notification(record, cell_value.repository_cell)
# end
if errors.count > 0
return {
status: :error,
errors: errors,
nr_of_added: nr_of_added,
total_nr: total_nr
}
else
return {
status: :ok,
nr_of_added: nr_of_added,
total_nr: total_nr
}
end
end
private
def generate_new_columns(header)
rep_columns_names = self.repository_columns.pluck(:name).push('Name')
header.each do |cname|
next if rep_columns_names.include? cname
RepositoryColumn.create(repository: self, name: cname, data_type: 0)
end
end
def generate_file(filename, file_path)
case File.extname(filename)
when '.csv'

View file

@ -1,363 +0,0 @@
class ImportRepository
include ActionView::Helpers::TextHelper
def initialize(options)
@file = options.fetch(:file)
@repository = options.fetch(:repository)
@session = options.fetch(:session)
@sheet = @repository.open_spreadsheet(@file)
end
def data
# Get data (it will trigger any errors as well)
header = @sheet.row(1)
rows = []
rows << Hash[[header, @sheet.row(2)].transpose]
# Fill in fields for dropdown
available_fields = @repository.available_repository_fields.map do |name|
truncate(name.last, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN)
end
@temp_file = TempFile.new(session_id: @session.id, file: @file)
Data.new(header, rows, available_fields, @repository, @temp_file)
end
def too_large?
@file.size > Constants::FILE_MAX_SIZE_MB.megabytes
end
def empty?
@sheet.last_row.between?(0, 1)
end
def generated_temp_file?
# Save file for next step (importing)
@temp_file = TempFile.new(
session_id: @session.id,
file: @file
)
if @temp_file.save
@temp_file.destroy_obsolete
return true
end
end
Data = Struct.new(
:header, :rows, :available_fields, :repository, :temp_file
)
# def import_repository
# session[:return_to] ||= request.referer
#
# respond_to do |format|
# if params[:file_id]
# @temp_file = TempFile.find_by_id(params[:file_id])
#
# if @temp_file
# # Check if session_id is equal to prevent file stealing
# if @temp_file.session_id == session.id
# # Check if mappings exists or else we don't have anything to parse
# if params[:mappings]
# @sheet = Team.open_spreadsheet(@temp_file.file)
#
# # Check for duplicated values
# h1 = params[:mappings].clone.delete_if { |k, v| v.empty? }
# if h1.length == h1.invert.length
#
# # Check if there exist mapping for sample name (it's mandatory)
# if params[:mappings].has_value?("-1")
# result = @team.import_samples(@sheet, params[:mappings], current_user)
# nr_of_added = result[:nr_of_added]
# total_nr = result[:total_nr]
#
# if result[:status] == :ok
# # If no errors are present, redirect back
# # to samples table
# flash[:success] = t(
# "teams.import_samples.success_flash",
# nr: nr_of_added,
# samples: t(
# "teams.import_samples.sample",
# count: total_nr
# )
# )
# @temp_file.destroy
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:success)
# render json: { status: :ok }
# }
# else
# # Otherwise, also redirect back,
# # but display different message
# flash[:alert] = t(
# "teams.import_samples.partial_success_flash",
# nr: nr_of_added,
# samples: t(
# "teams.import_samples.sample",
# count: total_nr
# )
# )
# @temp_file.destroy
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# # This is currently the only AJAX error response
# flash_alert = t(
# "teams.import_samples.errors.no_sample_name")
# format.html {
# flash[:alert] = flash_alert
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {
# html: render_to_string({
# partial: "parse_error.html.erb",
# locals: { error: flash_alert }
# })
# },
# status: :unprocessable_entity
# }
# end
# else
# # This code should never execute unless user tampers with
# # JS (selects same column in more than one dropdown)
# flash_alert = t(
# "teams.import_samples.errors.duplicated_values")
# format.html {
# flash[:alert] = flash_alert
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {
# html: render_to_string({
# partial: "parse_error.html.erb",
# locals: { error: flash_alert }
# })
# },
# status: :unprocessable_entity
# }
# end
# else
# @temp_file.destroy
# flash[:alert] = t(
# "teams.import_samples.errors.no_data_to_parse")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# @temp_file.destroy
# flash[:alert] = t(
# "teams.import_samples.errors.session_expired")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# # No temp file to begin with, so no need to destroy it
# flash[:alert] = t(
# "teams.import_samples.errors.temp_file_not_found")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# flash[:alert] = t(
# "teams.import_samples.errors.temp_file_not_found")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# end
# end
#
# def parse_sheet
# session[:return_to] ||= request.referer
#
# respond_to do |format|
# if params[:file]
# begin
#
# if params[:file].size > Constants::FILE_MAX_SIZE_MB.megabytes
# error = t 'general.file.size_exceeded',
# file_size: Constants::FILE_MAX_SIZE_MB
#
# format.html {
# flash[:alert] = error
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
#
# else
# sheet = Team.open_spreadsheet(params[:file])
#
# # Check if we actually have any rows (last_row > 1)
# if sheet.last_row.between?(0, 1)
# flash[:notice] = t(
# "teams.parse_sheet.errors.empty_file")
# redirect_to session.delete(:return_to) and return
# end
#
# # Get data (it will trigger any errors as well)
# @header = sheet.row(1)
# @rows = [];
# @rows << Hash[[@header, sheet.row(2)].transpose]
#
# # Fill in fields for dropdown
# @available_fields = @team.get_available_sample_fields
# # Truncate long fields
# @available_fields.update(@available_fields) do |_k, v|
# v.truncate(Constants::NAME_TRUNCATION_LENGTH_DROPDOWN)
# end
#
# # Save file for next step (importing)
# @temp_file = TempFile.new(
# session_id: session.id,
# file: params[:file]
# )
#
# if @temp_file.save
# @temp_file.destroy_obsolete
# # format.html
# format.json {
# render :json => {
# :html => render_to_string({
# :partial => "samples/parse_samples_modal.html.erb"
# })
# }
# }
# else
# error = t("teams.parse_sheet.errors.temp_file_failure")
# format.html {
# flash[:alert] = error
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# end
# end
# rescue ArgumentError, CSV::MalformedCSVError
# error = t('teams.parse_sheet.errors.invalid_file',
# encoding: ''.encoding)
# format.html {
# flash[:alert] = error
# redirect_to session.delete(:return_to)
# }
# redirect_to session.delete(:return_to)
# }
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# rescue TypeError
# error = t("teams.parse_sheet.errors.invalid_extension")
# format.html {
# flash[:alert] = error
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# end
# else
# error = t("teams.parse_sheet.errors.no_file_selected")
# format.html {
# flash[:alert] = error
# session[:return_to] ||= request.referer
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# end
# end
# end format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# rescue TypeError
# error = t("teams.parse_sheet.errors.invalid_extension")
# format.html {
# flash[:alert] = error
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# end
# else
# error = t("teams.parse_sheet.errors.no_file_selected")
# format.html {
# flash[:alert] = error
# session[:return_to] ||= request.referer
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# end
# end
# end format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# rescue TypeError
# error = t("teams.parse_sheet.errors.invalid_extension")
# format.html {
# flash[:alert] = error
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# end
# else
# error = t("teams.parse_sheet.errors.no_file_selected")
# format.html {
# flash[:alert] = error
# session[:return_to] ||= request.referer
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {message: error},
# status: :unprocessable_entity
# }
# end
# end
# end
end

View file

@ -0,0 +1,183 @@
module ImportRepository
class ImportRecords
def initialize(options)
@temp_file = options.fetch(:temp_file)
@repository = options.fetch(:repository)
@mappings = options.fetch(:mappings)
@session = options.fetch(:session)
@user = options.fetch(:user)
end
def has_temp_file?
@temp_file
end
def session_valid?
@temp_file.session_id == session.id
end
def import!
unless @mappings
return { error: t('teams.import_samples.errors.no_data_to_parse') }
end
sheet = @repository.open_spreadsheet(@temp_file.file)
# Check for duplicated values
h1 = @mappings.clone.delete_if { |k, v| v.empty? }
unless @mappings.has_value?('-1')
return { error: t('teams.import_samples.errors.no_sample_name') }
end
result = @repository.import_records(sheet, @mappings, @user)
end
end
end
# def import_repository
# session[:return_to] ||= request.referer
#
# respond_to do |format|
# if params[:file_id]
# @temp_file = TempFile.find_by_id(params[:file_id])
#
# if @temp_file
# # Check if session_id is equal to prevent file stealing
# if @temp_file.session_id == session.id
# # Check if mappings exists or else we don't have anything to parse
# if params[:mappings]
# @sheet = Team.open_spreadsheet(@temp_file.file)
#
# # Check for duplicated values
# h1 = params[:mappings].clone.delete_if { |k, v| v.empty? }
# if h1.length == h1.invert.length
#
# # Check if there exist mapping for sample name (it's mandatory)
# if params[:mappings].has_value?("-1")
# result = @team.import_samples(@sheet, params[:mappings], current_user)
# nr_of_added = result[:nr_of_added]
# total_nr = result[:total_nr]
#
# if result[:status] == :ok
# # If no errors are present, redirect back
# # to samples table
# flash[:success] = t(
# "teams.import_samples.success_flash",
# nr: nr_of_added,
# samples: t(
# "teams.import_samples.sample",
# count: total_nr
# )
# )
# @temp_file.destroy
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:success)
# render json: { status: :ok }
# }
# else
# # Otherwise, also redirect back,
# # but display different message
# flash[:alert] = t(
# "teams.import_samples.partial_success_flash",
# nr: nr_of_added,
# samples: t(
# "teams.import_samples.sample",
# count: total_nr
# )
# )
# @temp_file.destroy
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# # This is currently the only AJAX error response
# flash_alert = t(
# "teams.import_samples.errors.no_sample_name")
# format.html {
# flash[:alert] = flash_alert
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {
# html: render_to_string({
# partial: "parse_error.html.erb",
# locals: { error: flash_alert }
# })
# },
# status: :unprocessable_entity
# }
# end
# else
# # This code should never execute unless user tampers with
# # JS (selects same column in more than one dropdown)
# flash_alert = t(
# "teams.import_samples.errors.duplicated_values")
# format.html {
# flash[:alert] = flash_alert
# redirect_to session.delete(:return_to)
# }
# format.json {
# render json: {
# html: render_to_string({
# partial: "parse_error.html.erb",
# locals: { error: flash_alert }
# })
# },
# status: :unprocessable_entity
# }
# end
# else
# @temp_file.destroy
# flash[:alert] = t(
# "teams.import_samples.errors.no_data_to_parse")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# @temp_file.destroy
# flash[:alert] = t(
# "teams.import_samples.errors.session_expired")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# # No temp file to begin with, so no need to destroy it
# flash[:alert] = t(
# "teams.import_samples.errors.temp_file_not_found")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# else
# flash[:alert] = t(
# "teams.import_samples.errors.temp_file_not_found")
# format.html {
# redirect_to session.delete(:return_to)
# }
# format.json {
# flash.keep(:alert)
# render json: { status: :unprocessable_entity }
# }
# end
# end
# end

View file

@ -0,0 +1,53 @@
module ImportRepository
class ParseRepository
include ActionView::Helpers::TextHelper
def initialize(options)
@file = options.fetch(:file)
@repository = options.fetch(:repository)
@session = options.fetch(:session)
@sheet = @repository.open_spreadsheet(@file)
end
def data
# Get data (it will trigger any errors as well)
header = @sheet.row(1)
rows = []
rows << Hash[[header, @sheet.row(2)].transpose]
# Fill in fields for dropdown
@repository.available_repository_fields.transform_values! do |name|
truncate(name, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN)
end
@temp_file = TempFile.create(session_id: @session.id, file: @file)
Data.new(header,
rows,
@repository.available_repository_fields,
@repository,
@temp_file)
end
def too_large?
@file.size > Constants::FILE_MAX_SIZE_MB.megabytes
end
def empty?
@sheet.last_row.between?(0, 1)
end
def generated_temp_file?
# Save file for next step (importing)
@temp_file = TempFile.new(
session_id: @session.id,
file: @file
)
if @temp_file.save
@temp_file.destroy_obsolete
return true
end
end
Data = Struct.new(
:header, :rows, :available_fields, :repository, :temp_file
)
end
end

View file

@ -1,66 +1,72 @@
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title"><%= t('samples.modal_import.title') %></h4>
</div>
<%= bootstrap_form_tag(url: import_records_repository_path(@import_data.repository, format: :json),
html: {'data-type' => 'json', id: 'form-import'},
remote: true) do |f|%>
<div class="modal-body">
<h4><%= t("teams.parse_sheet.help_text") %></h4>
<div style="overflow-x: scroll">
<table class="table" style="display: block">
<thead>
<th style="min-width: 100px">
<p><%= t("teams.parse_sheet.scinote_columns_html") %></p>
<p style="margin-bottom: 0px"><%= t("teams.parse_sheet.file_columns") %></p>
</th>
<% @import_data.header.each.with_index do |th, index| %>
<th style="min-width: 150px" title="<%= th %>">
<%= f.select("mappings[#{index.to_s}]",
options_from_collection_for_select(@import_data.available_fields,
:first,
:last),
include_blank: t('teams.parse_sheet.do_not_include_column'),
hide_label: true) %>
<br />
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
<div class="modal-tooltip">
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
</div>
<% else %>
<%= th %>
<% end %>
<div class="modal fade"
id="parse-records_modal"
aria-labelledby="parse-modal-title"
role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="parse-modal-title"><%= t('samples.modal_import.title') %></h4>
</div>
<%= bootstrap_form_tag(url: import_records_repository_path(@import_data.repository, format: :json),
html: {'data-type' => 'json', id: 'form-import'},
remote: true) do |f|%>
<%= f.hidden_field :team_id, value: current_team.id %>
<div class="modal-body">
<h4><%= t("teams.parse_sheet.help_text") %></h4>
<div style="overflow-x: scroll">
<table class="table" style="display: block">
<thead>
<th style="min-width: 100px">
<p><%= t("teams.parse_sheet.scinote_columns_html") %></p>
<p style="margin-bottom: 0px"><%= t("teams.parse_sheet.file_columns") %></p>
</th>
<% end %>
</thead>
<tbody>
<% @import_data.rows.each do |row| %>
<tr>
<td>
<p><%= t('teams.parse_sheet.example_value') %></p>
</td>
<% row.each do |td| %>
<% @import_data.header.each.with_index do |th, index| %>
<th style="min-width: 150px" title="<%= th %>">
<%= f.select("mappings[#{index.to_s}]",
options_from_collection_for_select(@import_data.available_fields,
:first,
:last),
include_blank: t('teams.parse_sheet.do_not_include_column'),
hide_label: true) %>
<br />
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
<div class="modal-tooltip">
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
</div>
<% else %>
<%= th %>
<% end %>
</th>
<% end %>
</thead>
<tbody>
<% @import_data.rows.each do |row| %>
<tr>
<td>
<%= td[1] %>
<p><%= t('teams.parse_sheet.example_value') %></p>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
<%= hidden_field_tag 'file_id', @import_data.temp_file.id %>
<% row.each do |td| %>
<td>
<%= td[1] %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
</div>
<%= hidden_field_tag 'file_id', @import_data.temp_file.id %>
<div id="import-errors-container">
<div id="import-errors-container">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><%= t('general.cancel')%></button>
<input type="submit" class="btn btn-primary" value="<%= t('teams.parse_sheet.import_samples') %>"</input>
</div>
<% end %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><%= t('general.cancel')%></button>
<input type="submit" class="btn btn-primary" value="<%= t('teams.parse_sheet.import_samples') %>"</input>
</div>
<% end %>
</div>
</div>

View file

@ -12,7 +12,7 @@
</button>
<% end %>
<% if can_import_repository_records(repository) %>
<button type="button" class="btn btn-default" id="importRecordsButton" data-toggle="modal" data-target="#modal-import-records">
<button type="button" class="btn btn-default" id="importRecordsButton">
<span class="glyphicon glyphicon-cloud-upload"></span>
<span class="hidden-xs"><%= t('repositories.import_records') %></span>
</button>

View file

@ -25,7 +25,6 @@
<% if can_create_repository(current_team) %>
href="<%= create_modal_team_repositories_path %>"
class='create-repository'
data-remote='true'
<% end %>>
<span class="glyphicon glyphicon-plus"></span>
<span class="hidden-xs">&nbsp;<%= t('repositories.index.add_new_repository_tab') %></span>