Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plant trial export download #685

Merged
merged 8 commits into from
Jan 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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