Implement CSV import and conversion to contestants
This commit is contained in:
@@ -45,15 +45,48 @@ class ContestantsController < ApplicationController
|
||||
def import
|
||||
authorize @contest
|
||||
|
||||
if params[:csv_import]
|
||||
@csv_import = CsvImport.new(params.require(:csv_import).permit(:file, :separator))
|
||||
if @csv_import.save
|
||||
@csv_import = CsvImport.new
|
||||
else
|
||||
render :import, status: :unprocessable_entity
|
||||
end
|
||||
@csv_import = CsvImport.new
|
||||
end
|
||||
|
||||
def upload_csv
|
||||
authorize @contest
|
||||
|
||||
@csv_import = CsvImport.new(params.require(:csv_import).permit(:file, :separator))
|
||||
if @csv_import.save
|
||||
redirect_to "/contests/#{@contest.id}/import/#{@csv_import.id}"
|
||||
else
|
||||
@csv_import = CsvImport.new
|
||||
render :import, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def convert_csv
|
||||
authorize @contest
|
||||
|
||||
@csv_import = CsvImport.find(params[:id])
|
||||
@content = JSON.parse(@csv_import.content)
|
||||
@form = Forms::CsvConversionForm.new
|
||||
end
|
||||
|
||||
def finalize_import
|
||||
authorize @contest
|
||||
|
||||
@csv_import = CsvImport.find(params[:id])
|
||||
@content = JSON.parse(@csv_import.content)
|
||||
all_params = params.require(:forms_csv_conversion_form)
|
||||
@form = Forms::CsvConversionForm.new(params.require(:forms_csv_conversion_form).permit(:email_column, :name_column))
|
||||
if @form.valid?
|
||||
@content.each_with_index do |row, i|
|
||||
if all_params["row_#{i}".to_sym] == "1"
|
||||
if @form.email_column == -1
|
||||
Contestant.create(name: row[@form.name_column], contest: @contest)
|
||||
else
|
||||
Contestant.create(name: row[@form.name_column], email: row[@form.email_column], contest: @contest)
|
||||
end
|
||||
end
|
||||
end
|
||||
redirect_to contest_path(@contest)
|
||||
else
|
||||
render :convert_csv, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
|
14
app/lib/forms.rb
Normal file
14
app/lib/forms.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
module Forms
|
||||
class CsvConversionForm
|
||||
include ActiveModel::Model
|
||||
include ActiveModel::Attributes
|
||||
include ActiveModel::Validations::Callbacks
|
||||
include ActiveRecord::Transactions
|
||||
|
||||
attribute :name_column, :integer
|
||||
attribute :email_column, :integer
|
||||
|
||||
validates :name_column, presence: true
|
||||
validates_numericality_of :name_column, greater_than: -1
|
||||
end
|
||||
end
|
@@ -3,23 +3,26 @@
|
||||
# Table name: csv_imports
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# separator :integer not null
|
||||
# content :string not null
|
||||
# separator :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
class CsvImport < ApplicationRecord
|
||||
enum :separator, { comma: ",", semicolon: ";" }, default: :comma
|
||||
enum :separator, { comma: ",", semicolon: ";" }, suffix: true, default: :comma
|
||||
|
||||
has_one_attached :file
|
||||
|
||||
validates :file, presence: true
|
||||
validate :acceptable_csv, on: :create
|
||||
|
||||
before_save :read_csv
|
||||
|
||||
def acceptable_csv
|
||||
return unless file.attached?
|
||||
|
||||
if file.blob.byte_size > 20
|
||||
errors.add(:file, "this csv file is too large, it must be under 20MB")
|
||||
if file.blob.byte_size > 5 * 1024 * 1024
|
||||
errors.add(:file, "this csv file is too large, it must be under 5MB")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -31,14 +34,16 @@ class CsvImport < ApplicationRecord
|
||||
begin
|
||||
csv = CSV.read(attachment_changes["file"].attachable.path, col_sep: separator)
|
||||
|
||||
logger = Logger.new(STDOUT)
|
||||
logger.info(csv)
|
||||
errors.add(:file, :empty) if csv.count < 1 || (csv.count == 1 && csv[0].count == 1 && csv[0][0] == "")
|
||||
rescue CSV::MalformedCSVError => e
|
||||
errors.add(:file, e.message)
|
||||
end
|
||||
end
|
||||
|
||||
def read_csv
|
||||
self.content = JSON.dump(CSV.read(attachment_changes["file"].attachable.path, col_sep: separator_for_database))
|
||||
end
|
||||
|
||||
def options_for_separator
|
||||
keys = self.class.separators.keys
|
||||
keys.map(&:humanize).zip(keys).to_h
|
||||
|
@@ -19,10 +19,18 @@ class ContestPolicy < ApplicationPolicy
|
||||
record.user.id == user.id || user.admin?
|
||||
end
|
||||
|
||||
def convert_csv?
|
||||
record.user.id == user.id || user.admin?
|
||||
end
|
||||
|
||||
def edit?
|
||||
record.user.id == user.id || user.admin?
|
||||
end
|
||||
|
||||
def finalize_import?
|
||||
record.user.id == user.id || user.admin?
|
||||
end
|
||||
|
||||
def update?
|
||||
record.user.id == user.id || user.admin?
|
||||
end
|
||||
@@ -38,4 +46,8 @@ class ContestPolicy < ApplicationPolicy
|
||||
def scoreboard?
|
||||
true
|
||||
end
|
||||
|
||||
def upload_csv?
|
||||
record.user.id == user.id || user.admin?
|
||||
end
|
||||
end
|
||||
|
40
app/views/contestants/convert_csv.html.slim
Normal file
40
app/views/contestants/convert_csv.html.slim
Normal file
@@ -0,0 +1,40 @@
|
||||
= form_with model: @form, url: "/contests/#{@contest.id}/import/#{@csv_import.id}" do |form|
|
||||
|
||||
.row.mb-3
|
||||
.col
|
||||
.form-floating
|
||||
= form.select :name_column, [[t("helpers.none"), -1]] + Array.new(@content[0].count) {|i| [t("helpers.field") + "_#{i}", i] }, {}, class: "form-select"
|
||||
= form.label :name_column
|
||||
= t("contestants.import.name_column")
|
||||
|
||||
.row.mb-3
|
||||
.col
|
||||
.form-floating
|
||||
= form.select :email_column, [[t("helpers.none"), -1]] + Array.new(@content[0].count) {|i| [t("helpers.field") + "_#{i}", i] }, {}, class: "form-select"
|
||||
= form.label :email_column
|
||||
= t("contestants.import.email_column")
|
||||
|
||||
.row.g-3
|
||||
.col
|
||||
table.table.table-striped.table-hover
|
||||
thead
|
||||
tr
|
||||
- @content[0].each_with_index do |_, i|
|
||||
th scope="col"
|
||||
= t("helpers.field") + "_#{i}"
|
||||
th scope="col"
|
||||
= t("contestants.import.import_column")
|
||||
tbody
|
||||
- @content.each_with_index do |row, i|
|
||||
tr scope="row"
|
||||
- row.each do |value|
|
||||
td
|
||||
= value
|
||||
td
|
||||
.form-check.form-switch
|
||||
= form.check_box "row_#{i}".to_sym, class: "form-check-input", checked: true
|
||||
|
||||
.row.g-3
|
||||
.col
|
||||
= form.submit t("helpers.buttons.confirm"), class: "btn btn-primary"
|
||||
|
Reference in New Issue
Block a user