mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-16 16:57:11 +08:00
first run
This commit is contained in:
parent
5ac0ed4505
commit
a865253a2e
8 changed files with 471 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
377
app/services/import_repository.rb
Normal file
377
app/services/import_repository.rb
Normal 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
|
23
app/views/repositories/_import_repository_records.html.erb
Normal file
23
app/views/repositories/_import_repository_records.html.erb
Normal 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">×</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>
|
|
@ -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 } %>
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Add table
Reference in a new issue