mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-11-10 17:36:33 +08:00
Merge pull request #665 from rekonder/aj-SCI-1275
Repositories UI - refactor export (background job) [SCI-1275]
This commit is contained in:
commit
cc8878433d
8 changed files with 211 additions and 30 deletions
|
@ -1,8 +1,8 @@
|
|||
GIT
|
||||
remote: git://github.com/einzige/sneaky-save.git
|
||||
revision: 03866e838f62a4b13e15784974fcc13e14cd9502
|
||||
remote: https://github.com/einzige/sneaky-save
|
||||
revision: e7c77674abe74d598dfd58db7c680dd85936f207
|
||||
specs:
|
||||
sneaky-save (0.1.1)
|
||||
sneaky-save (0.1.2)
|
||||
activerecord (>= 3.2.0)
|
||||
|
||||
GEM
|
||||
|
|
|
@ -214,6 +214,56 @@ setTimeout(function() {
|
|||
// Enables noSearchHidden plugin
|
||||
$.fn.dataTable.defaults.noSearchHidden = true;
|
||||
|
||||
$('form#form-export').submit(function() {
|
||||
var form = this;
|
||||
|
||||
if (currentMode === 'viewMode') {
|
||||
// Remove all hidden fields
|
||||
$(form).find('input[name=row_ids\\[\\]]').remove();
|
||||
$(form).find('input[name=header_ids\\[\\]]').remove();
|
||||
|
||||
// Append visible column information
|
||||
$('.active table#repository-table thead tr th').each(function() {
|
||||
var th = $(this);
|
||||
var val;
|
||||
switch ($(th).attr('id')) {
|
||||
case 'checkbox':
|
||||
val = -1;
|
||||
break;
|
||||
case 'row-name':
|
||||
val = -2;
|
||||
break;
|
||||
case 'added-by':
|
||||
val = -3;
|
||||
break;
|
||||
case 'added-on':
|
||||
val = -4;
|
||||
break;
|
||||
default:
|
||||
val = th.attr('id');
|
||||
}
|
||||
|
||||
if (val) {
|
||||
appendInput(form, val, 'header_ids[]');
|
||||
}
|
||||
});
|
||||
|
||||
// Append records
|
||||
$.each(rowsSelected, function(index, rowId) {
|
||||
appendInput(form, rowId, 'row_ids[]');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function appendInput(form, val, name) {
|
||||
$(form).append(
|
||||
$('<input>')
|
||||
.attr('type', 'hidden')
|
||||
.attr('name', name)
|
||||
.val(val)
|
||||
);
|
||||
}
|
||||
|
||||
function initRowSelection() {
|
||||
// Handle clicks on checkbox
|
||||
$('.dt-body-center .repository-row-selector').change(function(e) {
|
||||
|
@ -637,29 +687,38 @@ function updateButtons() {
|
|||
$('th').removeClass('disable-click');
|
||||
$('.repository-row-selector').removeClass('disabled');
|
||||
$('.repository-row-selector').prop('disabled', false);
|
||||
if (rowsSelected.length === 1) {
|
||||
$('#editRepositoryRecord').prop('disabled', false);
|
||||
$('#editRepositoryRecord').removeClass('disabled');
|
||||
$('#deleteRepositoryRecordsButton').prop('disabled', false);
|
||||
$('#deleteRepositoryRecordsButton').removeClass('disabled');
|
||||
$('#assignRepositoryRecords').removeClass('disabled');
|
||||
$('#assignRepositoryRecords').prop('disabled', false);
|
||||
$('#unassignRepositoryRecords').removeClass('disabled');
|
||||
$('#unassignRepositoryRecords').prop('disabled', false);
|
||||
} else if (rowsSelected.length === 0) {
|
||||
if (rowsSelected.length === 0) {
|
||||
$('#editRepositoryRecord').prop('disabled', true);
|
||||
$('#editRepositoryRecord').addClass('disabled');
|
||||
$('#deleteRepositoryRecordsButton').prop('disabled', true);
|
||||
$('#deleteRepositoryRecordsButton').addClass('disabled');
|
||||
$('#exportRepositoriesButton').addClass('disabled');
|
||||
$('#exportRepositoriesButton').prop('disabled', true);
|
||||
$('#exportRepositoriesButton').off('click');
|
||||
$('#export-repositories').off('click');
|
||||
$('#assignRepositoryRecords').addClass('disabled');
|
||||
$('#assignRepositoryRecords').prop('disabled', true);
|
||||
$('#unassignRepositoryRecords').addClass('disabled');
|
||||
$('#unassignRepositoryRecords').prop('disabled', true);
|
||||
} else {
|
||||
$('#editRepositoryRecord').prop('disabled', true);
|
||||
$('#editRepositoryRecord').addClass('disabled');
|
||||
if (rowsSelected.length === 1) {
|
||||
$('#editRepositoryRecord').prop('disabled', false);
|
||||
$('#editRepositoryRecord').removeClass('disabled');
|
||||
} else {
|
||||
$('#editRepositoryRecord').prop('disabled', true);
|
||||
$('#editRepositoryRecord').addClass('disabled');
|
||||
}
|
||||
$('#deleteRepositoryRecordsButton').prop('disabled', false);
|
||||
$('#deleteRepositoryRecordsButton').removeClass('disabled');
|
||||
$('#exportRepositoriesButton').removeClass('disabled');
|
||||
$('#exportRepositoriesButton').prop('disabled', false);
|
||||
$('#exportRepositoriesButton').on('click', function() {
|
||||
$('#exportRepositoryModal').modal('show');
|
||||
});
|
||||
$('#export-repositories').on('click', function() {
|
||||
animateSpinner(null, true);
|
||||
$('#form-export').submit();
|
||||
});
|
||||
$('#assignRepositoryRecords').removeClass('disabled');
|
||||
$('#assignRepositoryRecords').prop('disabled', false);
|
||||
$('#unassignRepositoryRecords').removeClass('disabled');
|
||||
|
@ -674,6 +733,9 @@ function updateButtons() {
|
|||
$('#addNewColumn').prop('disabled', true);
|
||||
$('#deleteRepositoryRecordsButton').addClass('disabled');
|
||||
$('#deleteRepositoryRecordsButton').prop('disabled', true);
|
||||
$('#exportRepositoriesButton').addClass('disabled');
|
||||
$('#exportRepositoriesButton').off('click');
|
||||
$('#export-repositories').off('click');
|
||||
$('#assignRepositoryRecords').addClass('disabled');
|
||||
$('#assignRepositoryRecords').prop('disabled', true);
|
||||
$('#unassignRepositoryRecords').addClass('disabled');
|
||||
|
@ -971,6 +1033,7 @@ function changeToEditMode() {
|
|||
if (!_.isEmpty(searchText)) {
|
||||
table.search(searchText).draw();
|
||||
}
|
||||
initRowSelection();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
class RepositoriesController < ApplicationController
|
||||
before_action :load_vars, except: :repository_table_index
|
||||
before_action :load_vars, except: %i(index create create_modal)
|
||||
before_action :load_parent_vars, except:
|
||||
%i(repository_table_index export_repository)
|
||||
before_action :check_view_all_permissions, only: :index
|
||||
before_action :check_view_permissions, only: :export_repository
|
||||
before_action :check_edit_and_destroy_permissions, only:
|
||||
%(destroy destroy_modal rename_modal update)
|
||||
%i(destroy destroy_modal rename_modal update)
|
||||
before_action :check_copy_permissions, only:
|
||||
%(copy_modal copy)
|
||||
%i(copy_modal copy)
|
||||
before_action :check_create_permissions, only:
|
||||
%(create_new_modal create)
|
||||
%i(create_new_modal create)
|
||||
|
||||
def index
|
||||
render('repositories/index')
|
||||
end
|
||||
|
||||
def show_tab
|
||||
@repository = Repository.find_by_id(params[:repository_id])
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
|
@ -62,7 +64,6 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def destroy_modal
|
||||
@repository = Repository.find(params[:repository_id])
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
|
@ -75,7 +76,6 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
@repository = Repository.find(params[:id])
|
||||
flash[:success] = t('repositories.index.delete_flash',
|
||||
name: @repository.name)
|
||||
@repository.destroy
|
||||
|
@ -83,7 +83,6 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def rename_modal
|
||||
@repository = Repository.find(params[:repository_id])
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: {
|
||||
|
@ -96,7 +95,6 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def update
|
||||
@repository = Repository.find(params[:id])
|
||||
old_name = @repository.name
|
||||
@repository.update_attributes(repository_params)
|
||||
|
||||
|
@ -116,7 +114,6 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def copy_modal
|
||||
@repository = Repository.find(params[:repository_id])
|
||||
@tmp_repository = Repository.new(
|
||||
team: @team,
|
||||
created_by: current_user,
|
||||
|
@ -134,7 +131,6 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
|
||||
def copy
|
||||
@repository = Repository.find(params[:repository_id])
|
||||
@tmp_repository = Repository.new(
|
||||
team: @team,
|
||||
created_by: current_user
|
||||
|
@ -169,7 +165,6 @@ class RepositoriesController < ApplicationController
|
|||
|
||||
# AJAX actions
|
||||
def repository_table_index
|
||||
@repository = Repository.find_by_id(params[:repository_id])
|
||||
if @repository.nil? || !can_view_repository(@repository)
|
||||
render_403
|
||||
else
|
||||
|
@ -185,9 +180,24 @@ class RepositoriesController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def export_repository
|
||||
if params[:row_ids] && params[:header_ids]
|
||||
generate_zip
|
||||
else
|
||||
flash[:alert] = t('zip_export.export_error')
|
||||
end
|
||||
redirect_to :back
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_vars
|
||||
repository_id = params[:id] || params[:repository_id]
|
||||
@repository = Repository.find_by_id(repository_id)
|
||||
render_404 unless @repository
|
||||
end
|
||||
|
||||
def load_parent_vars
|
||||
@team = Team.find_by_id(params[:team_id])
|
||||
render_404 unless @team
|
||||
@repositories = @team.repositories.order(created_at: :asc)
|
||||
|
@ -197,6 +207,10 @@ class RepositoriesController < ApplicationController
|
|||
render_403 unless can_view_team_repositories(@team)
|
||||
end
|
||||
|
||||
def check_view_permissions
|
||||
render_403 unless can_view_repository(@repository)
|
||||
end
|
||||
|
||||
def check_create_permissions
|
||||
render_403 unless can_create_repository(@team)
|
||||
end
|
||||
|
@ -212,4 +226,66 @@ class RepositoriesController < ApplicationController
|
|||
def repository_params
|
||||
params.require(:repository).permit(:name)
|
||||
end
|
||||
|
||||
def generate_zip
|
||||
# Fetch rows in the same order as in the currently viewed datatable
|
||||
ordered_row_ids = params[:row_ids]
|
||||
id_row_map = RepositoryRow.where(id: ordered_row_ids,
|
||||
repository: @repository)
|
||||
.index_by(&:id)
|
||||
ordered_rows = ordered_row_ids.collect { |id| id_row_map[id.to_i] }
|
||||
|
||||
zip = ZipExport.create(user: current_user)
|
||||
zip.generate_exportable_zip(
|
||||
current_user,
|
||||
to_csv(ordered_rows, params[:header_ids]),
|
||||
:repositories
|
||||
)
|
||||
end
|
||||
|
||||
def to_csv(rows, column_ids)
|
||||
require 'csv'
|
||||
|
||||
# Parse column names
|
||||
csv_header = []
|
||||
column_ids.each do |c_id|
|
||||
csv_header << case c_id.to_i
|
||||
when -1
|
||||
next
|
||||
when -2
|
||||
I18n.t('repositories.table.row_name')
|
||||
when -3
|
||||
I18n.t('repositories.table.added_by')
|
||||
when -4
|
||||
I18n.t('repositories.table.added_on')
|
||||
else
|
||||
column = RepositoryColumn.find_by_id(c_id)
|
||||
column ? column.name : nil
|
||||
end
|
||||
end
|
||||
|
||||
CSV.generate do |csv|
|
||||
csv << csv_header
|
||||
rows.each do |row|
|
||||
csv_row = []
|
||||
column_ids.each do |c_id|
|
||||
csv_row << case c_id.to_i
|
||||
when -1
|
||||
next
|
||||
when -2
|
||||
row.name
|
||||
when -3
|
||||
row.created_by.full_name
|
||||
when -4
|
||||
I18n.l(row.created_at, format: :full)
|
||||
else
|
||||
cell = row.repository_cells
|
||||
.find_by(repository_column_id: c_id)
|
||||
cell ? cell.value.data : nil
|
||||
end
|
||||
end
|
||||
csv << csv_row
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -102,8 +102,12 @@ class ZipExport < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# generates zip export file for samples
|
||||
def generate_samples_zip(tmp_dir, data, options = {})
|
||||
def generate_samples_zip(tmp_dir, data, _options = {})
|
||||
file = FileUtils.touch("#{tmp_dir}/export.csv").first
|
||||
File.open(file, 'wb') { |f| f.write(data) }
|
||||
end
|
||||
|
||||
def generate_repositories_zip(tmp_dir, data, _options = {})
|
||||
file = FileUtils.touch("#{tmp_dir}/export.csv").first
|
||||
File.open(file, 'wb') { |f| f.write(data) }
|
||||
end
|
||||
|
|
24
app/views/repositories/_export_repository_modal.html.erb
Normal file
24
app/views/repositories/_export_repository_modal.html.erb
Normal file
|
@ -0,0 +1,24 @@
|
|||
<div class="modal fade"
|
||||
id="exportRepositoryModal"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
aria-labelledby="modal-export-repository-label">
|
||||
<%= bootstrap_form_tag(url: export_repository_team_path(repository),
|
||||
html: { id: 'form-export' }) do |f| %>
|
||||
<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 'zip_export.modal_label' %></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<%=t('zip_export.repository_html', repository: repository.name) %>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type='button' class='btn btn-primary' data-dismiss='modal' id='export-repositories'> <%=t 'my_modules.repository.export' %> </button>
|
||||
<button type='button' class='btn btn-default' data-dismiss='modal' id='close-modal-export-repositories'><%= t('general.close')%></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
|
@ -1,5 +1,7 @@
|
|||
<%= render partial: "repositories/delete_record_modal.html.erb" %>
|
||||
<%= render partial: "repositories/delete_column_modal.html.erb" %>
|
||||
<%= render partial: 'repositories/export_repository_modal.html.erb',
|
||||
locals: { repository: repository } %>
|
||||
|
||||
<div id="alert-container"></div>
|
||||
|
||||
|
@ -12,6 +14,13 @@
|
|||
</button>
|
||||
<% end %>
|
||||
|
||||
<% if can_view_repository(repository) %>
|
||||
<a href="#" class="btn btn-default" id="exportRepositoriesButton">
|
||||
<span class="glyphicon glyphicon-cloud-download"></span>
|
||||
<span class="hidden-xs"><%= t("my_modules.repository.export") %></span>
|
||||
</a>
|
||||
<% end %>
|
||||
|
||||
<div id="datatables-buttons" style="display: inline;">
|
||||
<div id="repository-columns-dropdown" class="dropdown">
|
||||
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">
|
||||
|
|
|
@ -612,6 +612,7 @@ en:
|
|||
more_activities: "Load older activities"
|
||||
repository:
|
||||
head_title: "%{project} | %{module} | Custom repository %{repository}"
|
||||
export: 'Export'
|
||||
|
||||
experiments:
|
||||
new:
|
||||
|
@ -1692,12 +1693,15 @@ en:
|
|||
deleted: "(deleted)"
|
||||
|
||||
zip_export:
|
||||
modal_label: 'Export repository'
|
||||
notification_title: 'Your package is ready to be exported!'
|
||||
expired_title: 'The required file was expired!'
|
||||
expired_description: 'The downloadable file expires in 7 days after its creation.'
|
||||
export_error: 'An error occured.'
|
||||
modal_label: 'Export request received'
|
||||
modal_html: "<p>Your export request is being processed.</p><p>When completed we will <strong>send an email to %{email}</strong> inbox with a link to your exported samples. Note that the link will expire in 7 days.</p>"
|
||||
repository_html: '<p>You are about to export selected items in repository %{repository}</p> <br> Repository will be exported in a .csv file format. You will receive <strong>email with a link</strong> where you can download it.'
|
||||
export_error: "Error when creating zip export."
|
||||
|
||||
# This section contains general words that can be used in any parts of
|
||||
# application.
|
||||
tiny_mce:
|
||||
|
|
|
@ -155,6 +155,7 @@ Rails.application.routes.draw do
|
|||
post 'parse_sheet'
|
||||
post 'import_samples'
|
||||
post 'export_samples'
|
||||
post 'export_repository', to: 'repositories#export_repository'
|
||||
# Used for atwho (smart annotations)
|
||||
get 'atwho_users', to: 'at_who#users'
|
||||
get 'atwho_samples', to: 'at_who#samples'
|
||||
|
|
Loading…
Reference in a new issue