first run

This commit is contained in:
zmagod 2017-06-13 08:10:10 +02:00
parent 5ac0ed4505
commit a865253a2e
8 changed files with 471 additions and 0 deletions

View file

@ -185,6 +185,14 @@ class RepositoriesController < ApplicationController
end
end
def parse_sheet
byebug
# import_repository = ImportRepository.new(file: params, repository: params)
end
def import_repository
end
private
def load_vars

View file

@ -1090,6 +1090,10 @@ module PermissionHelper
is_normal_user_or_admin_of_team(repository.team)
end
def can_import_repository_records(repository)
is_normal_user_or_admin_of_team(repository.team)
end
def can_edit_repository_records(repository)
is_normal_user_or_admin_of_team(repository.team)
end

View file

@ -15,6 +15,28 @@ class Repository < ActiveRecord::Base
validates :team, presence: true
validates :created_by, presence: true
def open_spreadsheet(file)
filename = file.original_filename
file_path = file.path
if file.class == Paperclip::Attachment && file.is_stored_on_s3?
fa = file.fetch
file_path = fa.path
end
generate_file(filename, file_path)
end
def available_repository_fields
fields = {}
# First and foremost add sample name
fields['-1'] = I18n.t('samples.table.sample_name')
# Add all other custom columns
repository_columns.order(:created_at).each do |rc|
fields[rc.id] = rc.name
end
fields
end
def copy(created_by, name)
new_repo = nil
@ -41,4 +63,24 @@ class Repository < ActiveRecord::Base
# If everything is okay, return new_repo
new_repo
end
private
def generate_file(filename, file_path)
case File.extname(filename)
when '.csv'
Roo::CSV.new(file_path, extension: :csv)
when '.tdv'
Roo::CSV.new(file_path, nil, :ignore, csv_options: { col_sep: '\t' })
when '.txt'
# This assumption is based purely on biologist's habits
Roo::CSV.new(file_path, csv_options: { col_sep: '\t' })
when '.xls'
Roo::Excel.new(file_path)
when '.xlsx'
Roo::Excelx.new(file_path)
else
raise TypeError
end
end
end

View file

@ -0,0 +1,377 @@
class ImportRepository
def initialize(options)
@file = options.fetch(:file)
@repository = options.fetch(:repository)
@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|
name.truncate(Constants::NAME_TRUNCATION_LENGTH_DROPDOWN)
end
{ header: header, rows: rows, available_fields: available_fields }
end
def check_file_size
return unless @file.size > Constants::FILE_MAX_SIZE_MB.megabytes
respond_to do |format|
error = t('general.file.size_exceeded',
file_size: Constants::FILE_MAX_SIZE_MB)
format.html do
flash[:alert] = error
redirect_to session.delete(:return_to)
end
format.json do
render json: { message: error },
status: :unprocessable_entity
end
end
end
def check_spreadsheet_rows(sheet)
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
end
def generate_temp_file
# Save file for next step (importing)
@temp_file = TempFile.new(
session_id: session.id,
file: @file
)
respond_to do |format|
if @temp_file.save
@temp_file.destroy_obsolete
# format.html
format.json do
render json: {
html: render_to_string({
partial: 'samples/parse_samples_modal.html.erb'
})
}
end
else
error = t('teams.parse_sheet.errors.temp_file_failure')
format.html do
flash[:alert] = error
redirect_to session.delete(:return_to)
end
format.json do
render json: { message: error },
status: :unprocessable_entity
end
end
end
end
def argument_error_callback
error = t('teams.parse_sheet.errors.invalid_file',
encoding: ''.encoding)
respond_to do |format|
format.html do
flash[:alert] = error
redirect_to session.delete(:return_to)
end
format.json do
render json: { message: error },
status: :unprocessable_entity
end
end
end
def type_error_callback
error = t('teams.parse_sheet.errors.invalid_extension')
respond_to do |format|
format.html do
flash[:alert] = error
redirect_to session.delete(:return_to)
end
format.json do
render json: {message: error},
status: :unprocessable_entity
end
end
end
def no_file_callback
error = t('teams.parse_sheet.errors.no_file_selected')
respond_to do |format|
format.html do
flash[:alert] = error
session[:return_to] ||= request.referer
redirect_to session.delete(:return_to)
end
format.json do
render json: { message: error },
status: :unprocessable_entity
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
#
# 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)
# }
# 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,23 @@
<div class="modal fade" id="modal-import-records" tabindex="-1" role="dialog" aria-labelledby="modal-import-records-label">
<div class="modal-dialog" 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('repositories.modal_import.title') %></h4>
<%= t("repositories.modal_import.notice") %>
</div>
<%= bootstrap_form_tag url: parse_sheet_repository_path(repository, format: :json),
html: {'data-type' => 'json',
id: 'form-samples-file'},
remote: :true do |f| %>
<div class="modal-body">
<%= f.file_field :file %>
</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("repositories.modal_import.upload") %>"</input>
</div>
<% end %>
</div>
</div>
</div>

View file

@ -11,6 +11,12 @@
<span class="hidden-xs"><%= t("repositories.add_new_record") %></span>
</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">
<span class="glyphicon glyphicon-cloud-upload"></span>
<span class="hidden-xs"><%= t('repositories.import_records') %></span>
</button>
<% end %>
<div id="datatables-buttons" style="display: inline;">
<div id="repository-columns-dropdown" class="dropdown">
@ -73,3 +79,5 @@
repository_index_link: repository_table_index_path(repository)
}
%>
<%= render partial: 'repositories/import_repository_records.html.erb',
locals: { repository: repository } %>

View file

@ -887,6 +887,7 @@ en:
added_on: "Added on"
added_by: "Added by"
add_new_record: "Add record"
import_records: 'Import'
edit_record: "Edit"
delete_record: "Delete"
save_record: "Save"
@ -912,6 +913,10 @@ en:
alert_line_1: "you will lose information in this column for %{nr} records;"
alert_line_2: "the column will be deleted for all team members."
delete: "Delete column"
modal_import:
title: 'Import records'
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.'
upload: 'Upload file'
js:
permission_error: "You don't have permission to edit this record."
not_found_error: "This repository record does not exist."

View file

@ -445,6 +445,10 @@ Rails.application.routes.draw do
resources :repository_columns, only: %i(create edit update destroy)
resources :repository_rows, only: %i(create edit update)
member do
post 'parse_sheet'
post 'import_records'
end
end
get 'search' => 'search#index'