Skip to content

Commit

Permalink
Merge pull request #685 from TGAC/plant_trial_export_download
Browse files Browse the repository at this point in the history
Plant trial export download
  • Loading branch information
Tomasz Gubała authored Jan 2, 2017
2 parents e913994 + 455f576 commit f138160
Show file tree
Hide file tree
Showing 16 changed files with 368 additions and 84 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ gem 'hashie'
gem 'draper', '~> 2.1.0'
gem 'kaminari'
gem 'paperclip', '~> 4.3'
gem 'rubyzip'
gem 'typhoeus'
gem 'squeel'

Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ GEM
rspec-support (3.5.0)
ruby_parser (3.6.4)
sexp_processor (~> 4.1)
rubyzip (1.2.0)
safe_yaml (0.9.7)
sass (3.2.19)
sass-rails (4.0.5)
Expand Down Expand Up @@ -419,6 +420,7 @@ DEPENDENCIES
reform (~> 1.2.6)
rspec-html-matchers
rspec-rails (~> 3.5.0)
rubyzip
sass-rails (~> 4.0.3)
shoulda-matchers
spring
Expand Down
10 changes: 9 additions & 1 deletion app/assets/javascripts/data_grid_specific_columns.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,15 @@ window.configs =
,
targets: 'plant_trials_id_column'
render: (data, type, full, meta) ->
'<a href="trial_scorings/' + data + '">Show</a>'
'<a href="trial_scorings/' + data + '" ' +
'title="See all trial\'s trait scorings in a single table." data-toggle="tooltip">' +
'Show' +
'</a>' +
'</br>' +
'<a href="trial_scorings/' + data + '.zip" ' +
'title="Download zip file with full trial information." data-toggle="tooltip">' +
'Download' +
'</a>'
]

'population-loci':
Expand Down
7 changes: 6 additions & 1 deletion app/controllers/trial_scorings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ def show
end
render json: grid_data
end
format.zip do
send_data Submission::PlantTrialZipExporter.new.call(@plant_trial).read,
filename: "plant_trial_#{@plant_trial.plant_trial_name.parameterize('_')}.zip",
type: 'application/zip'
end
end
end

private

def prepare_grid_data
objects = @plant_trial.scoring_table_data(current_user.try(:id))
objects = @plant_trial.scoring_table_data(user_id: current_user.try(:id))
ApplicationDecorator.decorate(objects).as_grid_data
end

Expand Down
58 changes: 45 additions & 13 deletions app/models/plant_trial.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,35 +44,67 @@ class PlantTrial < ActiveRecord::Base
include TableData

# NOTE: this one works per-trial and provides data for so-called 'pivot' trial scoring table
def scoring_table_data(uid = nil)
def scoring_table_data(user_id: nil, extended: false)
ts = TraitScore.arel_table

psu_subquery = PlantScoringUnit.visible(uid)
td_subquery = TraitDescriptor.visible(uid)
psu_subquery = PlantScoringUnit.visible(user_id)
td_subquery = TraitDescriptor.visible(user_id)
pa_subquery = PlantAccession.visible(user_id)
if extended
pl_subquery = PlantLine.visible(user_id)
df_subquery = DesignFactor.visible(user_id)
end

all_scores = TraitScore.
joins {[
psu_subquery.as('plant_scoring_units').on { plant_scoring_unit_id == plant_scoring_units.id }.outer,
td_subquery.as('trait_descriptors').on { trait_descriptor_id == trait_descriptors.id }.outer
]}
joins {
[
psu_subquery.as('plant_scoring_units').on { plant_scoring_unit_id == plant_scoring_units.id }.outer,
pa_subquery.as('plant_accessions').on { plant_accessions.id == plant_scoring_units.plant_accession_id }.outer,
td_subquery.as('trait_descriptors').on { trait_descriptor_id == trait_descriptors.id }.outer
] +
(
extended ? [
pl_subquery.as('plant_lines').on { plant_lines.id == plant_accessions.plant_line_id }.outer,
df_subquery.as('design_factors').on { design_factors.id == plant_scoring_units.design_factor_id }.outer
] : []
)
}
all_scores = all_scores.
where(plant_scoring_units: { plant_trial_id: self.id }).
where(ts[:user_id].eq(uid).or(ts[:published].eq(true))).
where(ts[:user_id].eq(user_id).or(ts[:published].eq(true))).
order('plant_scoring_units.scoring_unit_name asc, trait_descriptors.id asc').
group_by(&:plant_scoring_unit_id)

plant_scoring_units.visible(uid).order('scoring_unit_name asc').map do |unit|
scores = all_scores[unit.id] || []

[unit.scoring_unit_name] +
plant_scoring_units.visible(user_id).order('scoring_unit_name asc').map do |plant_scoring_unit|
scores = all_scores[plant_scoring_unit.id] || []
plant_accession = plant_scoring_unit.plant_accession
plant_line = extended ? plant_scoring_unit.plant_accession.try(:plant_line) : nil

[plant_scoring_unit.scoring_unit_name,
plant_accession.try(:plant_accession)] +
(
extended ? [
plant_accession.try(:plant_line).try(:plant_line_name),
(plant_accession.try(:plant_variety) || plant_line.try(:plant_variety)).try(:plant_variety_name),
plant_accession.try(:plant_accession_derivation),
plant_accession.try(:originating_organisation),
plant_accession.try(:year_produced),
plant_accession.try(:date_harvested),
plant_scoring_unit.number_units_scored,
plant_scoring_unit.scoring_unit_sample_size,
plant_scoring_unit.scoring_unit_frame_size,
plant_scoring_unit.design_factor.try(:design_factors),
plant_scoring_unit.date_planted
] : []
) +
trait_descriptors.pluck(:id).map do |td_id|
scores_for_trait = scores.select{ |s| s.trait_descriptor_id == td_id.to_i}
(1..replicate_numbers[td_id.to_i]).map do |replicate_number|
replicate = scores_for_trait.detect{ |s| s.technical_replicate_number == replicate_number }
replicate ? replicate.score_value : '-'
end
end.flatten +
[unit.id]
[plant_scoring_unit.id]
end
end

Expand Down
13 changes: 8 additions & 5 deletions app/services/submission/exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,22 @@ def submitted_object
@submission.submitted_object
end

def generate_document(klass, query)
def generate_document(klass, query, column_names: nil)
column_names ||= klass.table_columns
data = klass.table_data(query: query).
map{ |r| r[0, klass.table_columns.size] }
map{ |r| r[0, column_names.size] }
return nil if data.empty?
CSV.generate(headers: true) do |csv|
csv << humanize_columns(klass.table_columns)
csv << humanize_columns(klass, column_names)
data.each { |row| csv << row }
end
end

def humanize_columns(column_names)
def humanize_columns(klass, column_names)
column_names.map do |column_name|
column_name.split(/ as /i)[-1]
column_name = column_name.split(/ as /i)[-1]
column_name = "#{klass.table_name}.#{column_name}" unless column_name.include? '.'
I18n.t("tables.#{column_name}")
end
end
end
33 changes: 18 additions & 15 deletions app/services/submission/plant_trial_exporter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ class Submission::PlantTrialExporter < Submission::Exporter
def documents
{
plant_trial: plant_trial,
plant_scoring_units: plant_scoring_units,
plant_accessions: plant_accessions,
trait_descriptors: trait_descriptors,
trait_scoring: trait_scoring
}.reject { |_,v| v.nil? }
Expand All @@ -13,17 +11,8 @@ def documents

def plant_trial
generate_document PlantTrial,
{ id: submitted_object.id }
end

def plant_scoring_units
generate_document PlantScoringUnit,
{ id: submitted_object.plant_scoring_units.pluck(:id) }
end

def plant_accessions
generate_document PlantAccession,
{ id: submitted_object.plant_scoring_units.pluck(:plant_accession_id) }
{ id: submitted_object.id },
column_names: PlantTrial.table_columns[0..-3]
end

def trait_descriptors
Expand All @@ -32,10 +21,24 @@ def trait_descriptors
end

def trait_scoring
data = submitted_object.scoring_table_data(@submission.user.id)
data = submitted_object.scoring_table_data(user_id: @submission.user.id, extended: true)
return nil if data.empty?
CSV.generate(headers: true) do |csv|
csv << ['Scoring unit name'] + submitted_object.decorate.trait_headers
csv << [
I18n.t('tables.plant_scoring_units.scoring_unit_name'),
I18n.t('tables.plant_accessions.plant_accession'),
I18n.t('tables.plant_lines.plant_line_name'),
I18n.t('tables.plant_varieties.plant_variety_name'),
I18n.t('tables.plant_accessions.plant_accession_derivation'),
I18n.t('tables.plant_accessions.originating_organisation'),
I18n.t('tables.plant_accessions.year_produced'),
I18n.t('tables.plant_accessions.date_harvested'),
I18n.t('tables.plant_scoring_units.number_units_scored'),
I18n.t('tables.plant_scoring_units.scoring_unit_sample_size'),
I18n.t('tables.plant_scoring_units.scoring_unit_frame_size'),
I18n.t('tables.design_factors.design_factors'),
I18n.t('tables.plant_scoring_units.date_planted')
] + submitted_object.decorate.trait_headers
data.each { |row| csv << row[0...-1] } # Drop the BIP internal id
end
end
Expand Down
18 changes: 18 additions & 0 deletions app/services/submission/plant_trial_zip_exporter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'zip'

class Submission::PlantTrialZipExporter
def call(plant_trial)
exporter = Submission::PlantTrialExporter.new(
OpenStruct.new(submitted_object: plant_trial, user: plant_trial.user)
)
compressed_filestream = Zip::OutputStream.write_buffer do |zos|
exporter.documents.each do |document_name, content|
filename = "#{document_name}.csv"
zos.put_next_entry filename
zos.print content
end
end
compressed_filestream.rewind
compressed_filestream
end
end
2 changes: 2 additions & 0 deletions app/views/trial_scorings/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
%tr
%th.plant_scoring_units_scoring_unit_name_column
= t 'tables.plant_scoring_units.scoring_unit_name'
%th.plant_accessions_plant_accession_column
= t 'tables.plant_accessions.plant_accession'
- @plant_trial.decorate.trait_headers.each do |header|
%th.trait_scores_score_value_column= header
%tbody
2 changes: 1 addition & 1 deletion config/locales/data_tables.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ en:
scoring_unit_frame_size: Frame size
date_planted: Date planted
plant_trials:
id: Scoring
id: Trait scoring
plant_trial_name: Trial name
plant_trial_description: Description
project_descriptor: Project
Expand Down
39 changes: 39 additions & 0 deletions spec/controllers/trial_scorings_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,44 @@
get :show, format: :json, id: plant_trial.id
end
end

context 'when called for zip format' do
let(:tds) { create_list(:trait_descriptor, 2) }
before(:each) {
psus = [
create(:plant_scoring_unit,
plant_accession: create(:plant_accession, plant_line: create(:plant_line, :with_variety)),
plant_trial: plant_trial,
scoring_unit_name: 'a'),
create(:plant_scoring_unit,
plant_accession: create(:plant_accession, :with_variety),
plant_trial: plant_trial,
scoring_unit_name: 'b')
]
create(:trait_score, trait_descriptor: tds[0], plant_scoring_unit: psus[0])
create(:trait_score, trait_descriptor: tds[1], plant_scoring_unit: psus[0])
create(:trait_score, trait_descriptor: tds[1], plant_scoring_unit: psus[1])
}

it 'produces a zip file' do
get :show, format: :zip, id: plant_trial.id
expect(response.content_type).to eq 'application/zip'
end

it 'compress three files in the zip file' do
get :show, format: :zip, id: plant_trial.id
file = Tempfile.new('plant_trial')
file.write(response.body)
file.close
Zip::File.open(file.path) do |zfile|
data = zfile.map do |entry|
[entry.name, entry.get_input_stream.read]
end
expect(data.map(&:first)).
to match_array %w(plant_trial.csv trait_descriptors.csv trait_scoring.csv)
expect(data.map{ |d| d[1].lines.size }).to match_array [2,3,3]
end
end
end
end
end
4 changes: 4 additions & 0 deletions spec/factories/plant_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,9 @@
plant_line.update_column(:user_id, nil)
end
end

trait :with_variety do
plant_variety
end
end
end
Loading

0 comments on commit f138160

Please sign in to comment.