mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-06 20:05:55 +08:00
Merge pull request #1014 from ZmagoD/zd_SCI_2025
Fix custom repositories export so it supports "list of values" custom column types [fixes SCI-2025]
This commit is contained in:
commit
f778415321
9 changed files with 228 additions and 67 deletions
|
@ -261,7 +261,7 @@ class RepositoriesController < ApplicationController
|
||||||
|
|
||||||
def export_repository
|
def export_repository
|
||||||
if params[:row_ids] && params[:header_ids]
|
if params[:row_ids] && params[:header_ids]
|
||||||
generate_zip
|
RepositoryZipExport.generate_zip(params, @repository, current_user)
|
||||||
else
|
else
|
||||||
flash[:alert] = t('zip_export.export_error')
|
flash[:alert] = t('zip_export.export_error')
|
||||||
end
|
end
|
||||||
|
@ -333,66 +333,4 @@ class RepositoriesController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
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, -2
|
|
||||||
next
|
|
||||||
when -3
|
|
||||||
I18n.t('repositories.table.row_name')
|
|
||||||
when -4
|
|
||||||
I18n.t('repositories.table.added_by')
|
|
||||||
when -5
|
|
||||||
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, -2
|
|
||||||
next
|
|
||||||
when -3
|
|
||||||
row.name
|
|
||||||
when -4
|
|
||||||
row.created_by.full_name
|
|
||||||
when -5
|
|
||||||
I18n.l(row.created_at, format: :full)
|
|
||||||
else
|
|
||||||
cell = row.repository_cells
|
|
||||||
.find_by(repository_column_id: c_id)
|
|
||||||
cell ? cell.value.formatted : nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
csv << csv_row
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,7 +13,11 @@ class RepositoryListValue < ApplicationRecord
|
||||||
validates :repository_cell, presence: true
|
validates :repository_cell, presence: true
|
||||||
|
|
||||||
def formatted
|
def formatted
|
||||||
return '' unless repository_list_item
|
data.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def data
|
||||||
|
return nil unless repository_list_item
|
||||||
repository_list_item.data
|
repository_list_item.data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
62
app/services/repository_zip_export.rb
Normal file
62
app/services/repository_zip_export.rb
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
require 'csv'
|
||||||
|
module RepositoryZipExport
|
||||||
|
def self.generate_zip(params, repository, current_user)
|
||||||
|
# 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 self.to_csv(rows, column_ids)
|
||||||
|
# Parse column names
|
||||||
|
csv_header = []
|
||||||
|
column_ids.each do |c_id|
|
||||||
|
csv_header << case c_id.to_i
|
||||||
|
when -1, -2
|
||||||
|
next
|
||||||
|
when -3
|
||||||
|
I18n.t('repositories.table.row_name')
|
||||||
|
when -4
|
||||||
|
I18n.t('repositories.table.added_by')
|
||||||
|
when -5
|
||||||
|
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, -2
|
||||||
|
next
|
||||||
|
when -3
|
||||||
|
row.name
|
||||||
|
when -4
|
||||||
|
row.created_by.full_name
|
||||||
|
when -5
|
||||||
|
I18n.l(row.created_at, format: :full)
|
||||||
|
else
|
||||||
|
cell = row.repository_cells
|
||||||
|
.find_by(repository_column_id: c_id)
|
||||||
|
cell ? cell.value.formatted : nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
csv << csv_row
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
6
spec/factories/repository_text_value.rb
Normal file
6
spec/factories/repository_text_value.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :repository_text_value do
|
||||||
|
created_by { User.first || association(:project_user) }
|
||||||
|
last_modified_by { User.first || association(:project_user) }
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,7 +6,6 @@ describe 'samples_to_repository_migration:run' do
|
||||||
let!(:team) { create :team, created_by: user }
|
let!(:team) { create :team, created_by: user }
|
||||||
let!(:user_team) { create :user_team, user: user, team: team }
|
let!(:user_team) { create :user_team, user: user, team: team }
|
||||||
let!(:my_module) { create :my_module }
|
let!(:my_module) { create :my_module }
|
||||||
let!(:samples_table) { create :samples_table, user: user, team: team }
|
|
||||||
let(:sample_types_names) { %w(type_one type_two type_three) }
|
let(:sample_types_names) { %w(type_one type_two type_three) }
|
||||||
let(:sample_group_names) { %w(group_one group_two group_three) }
|
let(:sample_group_names) { %w(group_one group_two group_three) }
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ RSpec.describe RepositoryListValue, type: :model do
|
||||||
it { should accept_nested_attributes_for(:repository_cell) }
|
it { should accept_nested_attributes_for(:repository_cell) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#data' do
|
describe '#formatted' do
|
||||||
let!(:repository) { create :repository }
|
let!(:repository) { create :repository }
|
||||||
let!(:repository_column) { create :repository_column, name: 'My column' }
|
let!(:repository_column) { create :repository_column, name: 'My column' }
|
||||||
let!(:repository_column) do
|
let!(:repository_column) do
|
||||||
|
@ -32,7 +32,7 @@ RSpec.describe RepositoryListValue, type: :model do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the data of a selected item' do
|
it 'returns the formatted data of a selected item' do
|
||||||
list_item = create :repository_list_item,
|
list_item = create :repository_list_item,
|
||||||
data: 'my item',
|
data: 'my item',
|
||||||
repository: repository,
|
repository: repository,
|
||||||
|
@ -71,4 +71,58 @@ RSpec.describe RepositoryListValue, type: :model do
|
||||||
expect(repository_list_value.reload.formatted).to eq ''
|
expect(repository_list_value.reload.formatted).to eq ''
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#data' do
|
||||||
|
let!(:repository) { create :repository }
|
||||||
|
let!(:repository_column) { create :repository_column, name: 'My column' }
|
||||||
|
let!(:repository_column) do
|
||||||
|
create :repository_column, data_type: :RepositoryListValue
|
||||||
|
end
|
||||||
|
let!(:repository_row) { create :repository_row, name: 'My row' }
|
||||||
|
let!(:repository_list_value) do
|
||||||
|
create :repository_list_value, repository_cell_attributes: {
|
||||||
|
repository_column: repository_column,
|
||||||
|
repository_row: repository_row
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the data of a selected item' do
|
||||||
|
list_item = create :repository_list_item,
|
||||||
|
data: 'my item',
|
||||||
|
repository: repository,
|
||||||
|
repository_column: repository_column
|
||||||
|
repository_list_value.repository_list_item = list_item
|
||||||
|
repository_list_value.save
|
||||||
|
expect(repository_list_value.reload.data).to eq 'my item'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'retuns only the the item related to the list' do
|
||||||
|
repository_row_two = create :repository_row, name: 'New row'
|
||||||
|
repository_list_value_two =
|
||||||
|
create :repository_list_value, repository_cell_attributes: {
|
||||||
|
repository_column: repository_column,
|
||||||
|
repository_row: repository_row_two
|
||||||
|
}
|
||||||
|
list_item = create :repository_list_item,
|
||||||
|
data: 'new item',
|
||||||
|
repository: repository,
|
||||||
|
repository_column: repository_column
|
||||||
|
repository_list_value.repository_list_item = list_item
|
||||||
|
expect(repository_list_value.reload.data).to_not eq 'my item'
|
||||||
|
expect(repository_list_value.data).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty string if no item selected' do
|
||||||
|
list_item = create :repository_list_item,
|
||||||
|
data: 'my item',
|
||||||
|
repository: repository,
|
||||||
|
repository_column: repository_column
|
||||||
|
expect(repository_list_value.reload.data).to eq nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty string if item does not exists' do
|
||||||
|
repository_list_value.repository_list_item = nil
|
||||||
|
expect(repository_list_value.reload.data).to eq nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,6 +58,14 @@ RSpec.configure do |config|
|
||||||
config.after(:each) do
|
config.after(:each) do
|
||||||
DatabaseCleaner.clean
|
DatabaseCleaner.clean
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.around(:each, type: :background_job) do |example|
|
||||||
|
run_background_jobs_immediately do
|
||||||
|
example.run
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
config.include BackgroundJobs
|
||||||
# RSpec Rails can automatically mix in different behaviours to your tests
|
# RSpec Rails can automatically mix in different behaviours to your tests
|
||||||
# based on their file location, for example enabling you to call `get` and
|
# based on their file location, for example enabling you to call `get` and
|
||||||
# `post` in specs under `spec/controllers`.
|
# `post` in specs under `spec/controllers`.
|
||||||
|
|
82
spec/services/repository_zip_export_spec.rb
Normal file
82
spec/services/repository_zip_export_spec.rb
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
require 'zip'
|
||||||
|
|
||||||
|
describe RepositoryZipExport, type: :background_job do
|
||||||
|
let(:user) { create :user }
|
||||||
|
let(:team) { create :team, created_by: user }
|
||||||
|
let(:user_team) { create :user_team, user: user, team: team }
|
||||||
|
let(:repository) { create :repository, team: team, created_by: user }
|
||||||
|
let!(:sample_group_column) do
|
||||||
|
create :repository_column, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Sample group',
|
||||||
|
data_type: 'RepositoryListValue'
|
||||||
|
end
|
||||||
|
let!(:repository_list_item) do
|
||||||
|
create :repository_list_item, data: 'item one',
|
||||||
|
repository: repository,
|
||||||
|
repository_column: sample_group_column
|
||||||
|
end
|
||||||
|
let!(:custom_column) do
|
||||||
|
create :repository_column, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Custom items',
|
||||||
|
data_type: 'RepositoryTextValue'
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
@row_ids = []
|
||||||
|
10.times do |index|
|
||||||
|
row = create :repository_row, name: "row #{index}",
|
||||||
|
repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
create :repository_list_value, repository_list_item: repository_list_item,
|
||||||
|
repository_cell_attributes: {
|
||||||
|
repository_row: row,
|
||||||
|
repository_column: sample_group_column
|
||||||
|
}
|
||||||
|
create :repository_text_value, data: 'custum column value',
|
||||||
|
repository_cell_attributes: {
|
||||||
|
repository_row: row,
|
||||||
|
repository_column: custom_column
|
||||||
|
}
|
||||||
|
|
||||||
|
@row_ids << row.id.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#generate_zip/2' do
|
||||||
|
let(:params) do
|
||||||
|
{ header_ids: ['-1',
|
||||||
|
'-2',
|
||||||
|
sample_group_column.id.to_s,
|
||||||
|
custom_column.id.to_s,
|
||||||
|
'-3',
|
||||||
|
'-5',
|
||||||
|
'-4'],
|
||||||
|
row_ids: @row_ids }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generates a new zip export object' do
|
||||||
|
RepositoryZipExport.generate_zip(params, repository, user)
|
||||||
|
expect(ZipExport.count).to eq 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generates a zip with csv file with exported rows' do
|
||||||
|
RepositoryZipExport.generate_zip(params, repository, user)
|
||||||
|
zip = ZipExport.first.zip_file_file_name
|
||||||
|
.gsub('export-', '')
|
||||||
|
.gsub('.zip', '')
|
||||||
|
csv_path = Rails.root.join('tmp', "temp-zip-#{zip}", 'export.csv').to_s
|
||||||
|
index = 0
|
||||||
|
CSV.foreach(csv_path, headers: true) do |row|
|
||||||
|
row_hash = row.to_h
|
||||||
|
expect(row_hash.fetch('Sample group')).to eq 'item one'
|
||||||
|
expect(row_hash.fetch('Custom items')).to eq 'custum column value'
|
||||||
|
expect(row_hash.fetch('Name')).to eq "row #{index}"
|
||||||
|
index += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
spec/support/background_job.rb
Normal file
8
spec/support/background_job.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module BackgroundJobs
|
||||||
|
def run_background_jobs_immediately
|
||||||
|
delay_jobs = Delayed::Worker.delay_jobs
|
||||||
|
Delayed::Worker.delay_jobs = false
|
||||||
|
yield
|
||||||
|
Delayed::Worker.delay_jobs = delay_jobs
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue