Export repository [aj-SCI-1275]

This commit is contained in:
rekonder 2017-06-06 16:12:34 +02:00 committed by Matej Zrimšek
parent e26337d5ee
commit ede744849b
8 changed files with 195 additions and 7 deletions

View file

@ -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

View file

@ -214,6 +214,54 @@ setTimeout(function() {
// Enables noSearchHidden plugin
$.fn.dataTable.defaults.noSearchHidden = true;
$('form#form-export').submit(function(e) {
var form = this;
if (currentMode === 'viewMode') {
// Remove all hidden fields
$('#form-export').find('input[name=row_ids\\[\\]]').remove();
$('#form-export').find('input[name=header_ids\\[\\]]').remove();
// Append samples
appendSamplesIdToForm(form);
// Append visible column information
$('table#repository-table thead tr').children('th').each(function(i) {
var th = $(this);
var val;
if ($(th).attr('id') === 'checkbox' || $(th).attr('id') === 'assigned')
val = -1
else if ($(th).attr('id') === 'row-name')
val = -2;
else if ($(th).attr('id') === 'added-by')
val = -3;
else if ($(th).attr('id') === 'added-on')
val = -4;
else
val = th.attr('id');
if (val)
$(form).append(
$('<input>')
.attr('type', 'hidden')
.attr('name', 'header_ids[]')
.val(val)
);
});
}
});
function appendSamplesIdToForm(form) {
$.each(rowsSelected, function(index, rowId) {
$(form).append(
$('<input>')
.attr('type', 'hidden')
.attr('name', 'row_ids[]')
.val(rowId)
);
});
}
function initRowSelection() {
// Handle clicks on checkbox
$('.dt-body-center .repository-row-selector').change(function(e) {
@ -642,6 +690,16 @@ function updateButtons() {
$('#editRepositoryRecord').removeClass('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');
@ -651,6 +709,10 @@ function updateButtons() {
$('#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');
@ -660,6 +722,16 @@ function updateButtons() {
$('#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 +746,8 @@ function updateButtons() {
$('#addNewColumn').prop('disabled', true);
$('#deleteRepositoryRecordsButton').addClass('disabled');
$('#deleteRepositoryRecordsButton').prop('disabled', true);
$('#exportRepositoriesButton').addClass('disabled');
$('#exportRepositoriesButton').off('click');
$('#assignRepositoryRecords').addClass('disabled');
$('#assignRepositoryRecords').prop('disabled', true);
$('#unassignRepositoryRecords').addClass('disabled');

View file

@ -1,5 +1,5 @@
class RepositoriesController < ApplicationController
before_action :load_vars, except: :repository_table_index
before_action :load_vars, except: [:repository_table_index, :export_repository]
before_action :check_view_all_permissions, only: :index
before_action :check_edit_and_destroy_permissions, only:
%(destroy destroy_modal rename_modal update)
@ -7,6 +7,7 @@ class RepositoriesController < ApplicationController
%(copy_modal copy)
before_action :check_create_permissions, only:
%(create_new_modal create)
before_action :generate_zip, only: :export_repository
def index
render('repositories/index')
@ -185,6 +186,15 @@ 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
@ -212,4 +222,68 @@ class RepositoriesController < ApplicationController
def repository_params
params.require(:repository).permit(:name)
end
def generate_zip
zip = ZipExport.create(user: current_user)
zip.generate_exportable_zip(
current_user,
to_csv(RepositoryRow.where(id: params[:row_ids]), params[:header_ids]),
:repositories
)
end
def to_csv(rows, headers)
require 'csv'
# Parse headers (magic numbers should be refactored - see
# sample-datatable.js)
header_names = []
headers.each do |header|
if header == '-1'
next
elsif header == '-2'
header_names << I18n.t('repositories.table.row_name')
elsif header == '-3'
header_names << I18n.t('repositories.table.added_by')
elsif header == '-4'
header_names << I18n.t('repositories.table.added_on')
else
rc = RepositoryColumn.find_by_id(header)
if rc
header_names << rc.name
else
header_names << nil
end
end
end
CSV.generate do |csv|
csv << header_names
rows.each do |row|
sample_row = []
row_record = RepositoryRow.where(repository_rows: { id: row })
headers.each do |header|
if header == '-1'
next
elsif header == '-2'
sample_row << row.name
elsif header == '-3'
sample_row << row.created_by.full_name
elsif header == '-4'
sample_row << I18n.l(row.created_at, format: :full)
else
record = row_record.joins(:repository_columns, :repository_cells)
.where(repository_columns: { id: header })
.take
if record
sample_row << record.repository_cells.take.value.data
else
sample_row << nil
end
end
end
csv << sample_row
end
end
end
end

View file

@ -102,8 +102,7 @@ class ZipExport < ActiveRecord::Base
end
end
# generates zip export file for samples
def generate_samples_zip(tmp_dir, data, options = {})
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

View file

@ -0,0 +1,21 @@
<div class="modal fade"
id="exportRepositoryModal"
tabindex="-1"
role="dialog"
aria-labelledby="modal-export-repository-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 '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>
</div>

View file

@ -1,8 +1,16 @@
<%= 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>
<% if can_view_repository(repository) %>
<%= bootstrap_form_tag(url: export_repository_team_path(repository),
html: { id: 'form-export', class: 'hidden' }) do |f| %>
<% end %>
<% end %>
<div id="repository-toolbar">
<% if can_create_repository_records(repository) %>
@ -12,6 +20,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">

View file

@ -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:

View file

@ -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'