Skip to content
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
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ group :development, :test do
gem 'shoulda-matchers'
end

group :test do
gem 'roo'
end

gem "kaminari"
gem "friendly_id", "~> 5.1.0"
gem "jquery-rails"
gem 'axlsx'
17 changes: 16 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ GEM
minitest (~> 5.1)
tzinfo (~> 1.1)
arel (8.0.0)
axlsx (2.0.1)
htmlentities (~> 4.3.1)
nokogiri (>= 1.4.1)
rubyzip (~> 1.0.0)
builder (3.2.3)
byebug (11.0.1)
coderay (1.1.2)
Expand All @@ -68,6 +72,7 @@ GEM
haml (5.1.1)
temple (>= 0.8.0)
tilt
htmlentities (4.3.4)
i18n (1.6.0)
concurrent-ruby (~> 1.0)
jquery-rails (4.3.5)
Expand Down Expand Up @@ -135,6 +140,10 @@ GEM
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (13.0.0)
roo (1.13.2)
nokogiri
rubyzip
spreadsheet (> 0.6.4)
rspec-core (3.8.2)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.4)
Expand All @@ -152,8 +161,12 @@ GEM
rspec-mocks (~> 3.8.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.2)
ruby-ole (1.2.12.2)
rubyzip (1.0.0)
shoulda-matchers (4.0.1)
activesupport (>= 4.2.0)
spreadsheet (1.2.4)
ruby-ole (>= 1.0)
sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
Expand All @@ -175,6 +188,7 @@ PLATFORMS
ruby

DEPENDENCIES
axlsx
byebug
database_cleaner
dynamic_forms!
Expand All @@ -187,8 +201,9 @@ DEPENDENCIES
pry
pry-rails
rails-controller-testing
roo
rspec-rails
shoulda-matchers

BUNDLED WITH
1.16.2
2.0.2
25 changes: 21 additions & 4 deletions app/controllers/dynamic_forms/submissions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
module DynamicForms
class SubmissionsController < DynamicForms::ApplicationController
before_action :load_custom_form
before_action :load_submission, only: %w[destroy]
before_action :load_submission, only: %i[destroy]
before_action :load_submissions, only: %i[index export]

def index
@submissions = @custom_form.submissions
.order('created_at DESC')
.page(params[:page])
respond_to do |format|
format.html
format.csv { export_as('csv') }
format.xlsx { export_as('xlsx') }
end
end

def destroy
Expand All @@ -20,6 +23,7 @@ def destroy
redirect_to custom_form_submissions_path(@custom_form)
end


private

def load_custom_form
Expand All @@ -29,11 +33,24 @@ def load_custom_form
redirect_to custom_forms_path
end

def load_submissions
@submissions = @custom_form.submissions
.order('created_at DESC')
.page(params[:page])
end

def load_submission
@submission = @custom_form.submissions.find(params[:id])
rescue
flash[:error] = "Submission was not found"
redirect_to custom_form_path(@custom_form)
end

def export_as(type)
send_data(
SubmissionExporter.for(@custom_form.submissions.order('created_at'), type),
filename: "submissions-#{Date.today}.#{type}"
)
end
end
end
79 changes: 79 additions & 0 deletions app/services/dynamic_forms/submission_exporter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
require 'csv'

module DynamicForms
class SubmissionExporter
FORMAT_DATE = '%B %m, %Y, %H:%M'.freeze

def self.for(submissions, format_type)
new(submissions, format_type).export
end

def initialize(submissions, format_type)
raise TypeError, "Expected an string, got #{format_type.class.name}" unless format_type.is_a?(String)
raise ArgumentError, 'Expected submissions' if submissions.nil?

@submissions = submissions
@format_type = format_type
@header = build_header
end

def export
case @format_type
when 'csv'
to_csv
when 'xlsx'
to_xlsx
else
raise ArgumentError, 'This format is not available'
end
end

private

def to_csv
CSV.generate(headers: true) do |csv|
csv << @header

@submissions.each do |submission|
csv << build_row(submission)
end
end
end

def to_xlsx
xlsx = Axlsx::Package.new do |xlsx|
xlsx.workbook.add_worksheet(name: 'Submissions') do |sheet|
sheet.add_row @header

@submissions.each do |submission|
sheet.add_row build_row(submission)
end
end
end
xlsx.serialize('/tmp/submission_temp.xlsx')
xlsx.to_stream.read
end

def build_row(submission)
[].tap do |row|
row[0] = submission.created_at.strftime(FORMAT_DATE)
submission.fields.each do |key, value|
column_name = key.capitalize
column_index = @header.find_index(column_name)

row[column_index] = value
end
end
end

def build_header
['Created at'] + field_keys
end

def field_keys
Submission
.select('json_object_keys(fields) as field_keys')
.distinct.map{ |s| s.field_keys.capitalize }
end
end
end
6 changes: 6 additions & 0 deletions app/views/dynamic_forms/submissions/_export_as.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.dropdown
%button#dropdownMenuButton.btn.btn-link.dropdown-toggle{"aria-expanded" => "false", "aria-haspopup" => "true", "data-toggle" => "dropdown", :type => "button"}
Export as
.dropdown-menu{"aria-labelledby": "dropdownMenuButton"}
= link_to 'CSV', custom_form_submissions_path(@custom_form, format: 'csv'), class: 'dropdown-item'
= link_to 'XLSX', custom_form_submissions_path(@custom_form, format: 'xlsx'), class: 'dropdown-item'
4 changes: 3 additions & 1 deletion app/views/dynamic_forms/submissions/index.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
.col-12
.card.my-5
.card-header
%h2=@custom_form.name.titleize
.d-flex.justify-content-between
%h2=@custom_form.name.titleize
= render 'export_as'
.card-body
.row
.col-1.p-2.font-weight-bold
Expand Down
1 change: 1 addition & 0 deletions config/initializers/mime_types.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Mime::Type.register 'application/xlsx', :xlsx
2 changes: 1 addition & 1 deletion spec/dummy/config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ development:

test:
<<: *default
database: dynamic_forms
database: dynamic_forms_test

production:
<<: *default
72 changes: 72 additions & 0 deletions spec/services/dynamic_forms/submission_exporter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require 'rails_helper'

describe DynamicForms::SubmissionExporter do
let!(:submissions) { create_list(:submission, 5, created_at: '2019-10-08 19:39:06 UTC') }
let!(:with_different_field) {
create_list(
:submission,
5,
fields: { age: '12', phone: '1231432', gender: 'F' },
created_at: '2019-10-08 19:39:06 UTC'
)
}

let(:all_submissions) { DynamicForms::Submission.all }

context 'when submissions are not sent correclty' do
subject { DynamicForms::SubmissionExporter.for(nil, 'csv') }
it 'returns an ArgumentError' do
expect { subject }.to raise_error(ArgumentError)
end
end

context 'when the format type is not sent correctly' do
context 'when the format type is not string' do
subject { DynamicForms::SubmissionExporter.for(all_submissions, nil) }

it 'returns a TypeError' do
expect { subject }.to raise_error(TypeError)
end
end

context 'when the format type is not available' do
subject { DynamicForms::SubmissionExporter.for(all_submissions, 'pdf') }

it 'returns a ArgumentError' do
expect { subject }.to raise_error(ArgumentError)
end
end
end

context '#to_csv' do
subject { DynamicForms::SubmissionExporter.for(all_submissions, 'csv') }
let!(:rows) { subject.split("\n") }

it 'returns a csv content with 11 lines' do
expect(rows.size).to eq(11)
end

it 'includes the header' do
expect(rows.first).to eq('Created at,Gender,_subject,Phone,Age,Email,Name')
end

it 'includes data with fields correctly' do
expect(rows.slice(2)).to eq("\"October 10, 2019, 19:39\",,Test email,,,[email protected],John")
expect(rows.slice(6)).to eq("\"October 10, 2019, 19:39\",F,,1231432,12")
end
end

context '#to_xlsx' do
subject { DynamicForms::SubmissionExporter.for(all_submissions, 'xlsx') }
let!(:xlsx) { Roo::Excelx.new('/tmp/submission_temp.xlsx') }

it 'includes the header' do
expect(xlsx.row(1)).to eq(["Created at", "Gender", "_subject", "Phone", "Age", "Email", "Name"])
end

it 'includes correct data' do
expect(xlsx.row(2)).to eq(["October 10, 2019, 19:39", nil, "Test email", nil, nil, "[email protected]", "John"])
expect(xlsx.row(8)).to eq(["October 10, 2019, 19:39", "F", nil, 1231432.0, 12.0, nil, nil])
end
end
end