Finished CSV import
Fixed problem with excessive validation
This commit is contained in:
parent
eb43c4be21
commit
3ebc2f58d0
3
Gemfile
3
Gemfile
@ -1,7 +1,8 @@
|
|||||||
gem 'awesome_nested_set'
|
gem 'awesome_nested_set'
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
gem "web-console"
|
gem 'web-console'
|
||||||
|
gem 'active_record_query_trace'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
@ -9,7 +9,7 @@ class IngredientsController < ApplicationController
|
|||||||
@ingredient = Ingredient.new(project: @project)
|
@ingredient = Ingredient.new(project: @project)
|
||||||
# passing attr for after_initialize
|
# passing attr for after_initialize
|
||||||
@ingredient.nutrients.new(ingredient: @ingredient)
|
@ingredient.nutrients.new(ingredient: @ingredient)
|
||||||
@ingredients = @project.ingredients
|
@ingredients = @project.ingredients.includes(:ref_unit)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@ -18,7 +18,7 @@ class IngredientsController < ApplicationController
|
|||||||
flash[:notice] = 'Created new ingredient'
|
flash[:notice] = 'Created new ingredient'
|
||||||
redirect_to project_ingredients_url(@project)
|
redirect_to project_ingredients_url(@project)
|
||||||
else
|
else
|
||||||
@ingredients = @project.ingredients
|
@ingredients = @project.ingredients.includes(:ref_unit)
|
||||||
render :index
|
render :index
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -37,7 +37,7 @@ class IngredientsController < ApplicationController
|
|||||||
if params.has_key?(:file)
|
if params.has_key?(:file)
|
||||||
quantities = @project.quantities.map { |q| [q.name, q] }.to_h
|
quantities = @project.quantities.map { |q| [q.name, q] }.to_h
|
||||||
units = @project.units.map { |u| [u.shortname, u] }.to_h
|
units = @project.units.map { |u| [u.shortname, u] }.to_h
|
||||||
ingredients = []
|
ingredients_params = []
|
||||||
column_units = {}
|
column_units = {}
|
||||||
|
|
||||||
CSV.foreach(params[:file].path, headers: true).with_index(2) do |row, line|
|
CSV.foreach(params[:file].path, headers: true).with_index(2) do |row, line|
|
||||||
@ -45,7 +45,7 @@ class IngredientsController < ApplicationController
|
|||||||
unless r.has_key?('Name')
|
unless r.has_key?('Name')
|
||||||
warnings << "Line 1: required 'Name' column is missing" if line == 2
|
warnings << "Line 1: required 'Name' column is missing" if line == 2
|
||||||
end
|
end
|
||||||
i = {
|
i_params = {
|
||||||
name: r.delete('Name'),
|
name: r.delete('Name'),
|
||||||
ref_amount: 100.0,
|
ref_amount: 100.0,
|
||||||
ref_unit: units['g'],
|
ref_unit: units['g'],
|
||||||
@ -88,25 +88,34 @@ class IngredientsController < ApplicationController
|
|||||||
|
|
||||||
next if quantities[quantity].blank?
|
next if quantities[quantity].blank?
|
||||||
if quantity == 'Reference'
|
if quantity == 'Reference'
|
||||||
i.update({
|
i_params.update({
|
||||||
ref_amount: amount.to_d,
|
ref_amount: amount.to_d,
|
||||||
ref_unit: unit
|
ref_unit: unit
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
i[:nutrients_attributes] << {
|
i_params[:nutrients_attributes] << {
|
||||||
quantity: quantities[quantity],
|
quantity: quantities[quantity],
|
||||||
amount: amount.to_d,
|
amount: amount.to_d,
|
||||||
unit: unit
|
unit: unit
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ingredients << i
|
|
||||||
|
ingredients_params << i_params
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
warnings << 'No file selected'
|
warnings << 'No file selected'
|
||||||
end
|
end
|
||||||
|
|
||||||
if warnings.present?
|
if warnings.empty?
|
||||||
|
ingredients = @project.ingredients.create(ingredients_params)
|
||||||
|
flash[:notice] = "Imported #{ingredients.map(&:persisted?).count(true)} out of" \
|
||||||
|
" #{ingredients_params.length} ingredients"
|
||||||
|
skipped = ingredients.select { |i| !i.persisted? }
|
||||||
|
skipped_desc = skipped.map { |i| "#{i.name} - #{i.errors.full_messages.join(', ')}" }
|
||||||
|
flash[:warning] = "Ingredients skipped due to errors:<br>" \
|
||||||
|
" #{skipped_desc.join('<br>').truncate(1024)}"
|
||||||
|
else
|
||||||
warnings.unshift("Problems encountered during import - fix and try again:")
|
warnings.unshift("Problems encountered during import - fix and try again:")
|
||||||
flash[:warning] = warnings.join("<br>").truncate(1024, omission: '...(and other)')
|
flash[:warning] = warnings.join("<br>").truncate(1024, omission: '...(and other)')
|
||||||
end
|
end
|
||||||
|
@ -4,26 +4,24 @@ class Ingredient < ActiveRecord::Base
|
|||||||
meat: 1
|
meat: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
belongs_to :project
|
belongs_to :project, required: true
|
||||||
belongs_to :ref_unit, class_name: 'Unit'
|
belongs_to :ref_unit, class_name: 'Unit', required: true
|
||||||
|
|
||||||
has_many :nutrients, inverse_of: :ingredient, dependent: :destroy
|
has_many :nutrients, inverse_of: :ingredient, dependent: :destroy, validate: true
|
||||||
|
validates :nutrients, presence: true
|
||||||
accepts_nested_attributes_for :nutrients, allow_destroy: true, reject_if: proc { |attrs|
|
accepts_nested_attributes_for :nutrients, allow_destroy: true, reject_if: proc { |attrs|
|
||||||
attrs['quantity_id'].blank? && attrs['amount'].blank?
|
attrs['quantity_id'].blank? && attrs['amount'].blank?
|
||||||
}
|
}
|
||||||
validates_associated :nutrients
|
|
||||||
# Nutrient quantity_id uniqueness check for nested attributes
|
# Nutrient quantity_id uniqueness check for nested attributes
|
||||||
validate on: :create do
|
validate do
|
||||||
quantities = self.nutrients.map { |n| n.quantity_id }
|
quantities = self.nutrients.map { |n| n.quantity_id }
|
||||||
if quantities.length != quantities.uniq.length
|
if quantities.length != quantities.uniq.length
|
||||||
errors.add(:nutrients, :duplicated_quantity)
|
errors.add(:nutrients, :duplicated_quantity)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
validates :project, associated: true
|
|
||||||
validates :name, presence: true, uniqueness: {scope: :project_id}
|
validates :name, presence: true, uniqueness: {scope: :project_id}
|
||||||
validates :ref_amount, numericality: {greater_than: 0}
|
validates :ref_amount, numericality: {greater_than: 0}
|
||||||
validates :ref_unit, presence: true, associated: true
|
|
||||||
validates :group, inclusion: {in: groups.keys}
|
validates :group, inclusion: {in: groups.keys}
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
@ -32,6 +30,7 @@ class Ingredient < ActiveRecord::Base
|
|||||||
units = self.project.units
|
units = self.project.units
|
||||||
self.ref_unit ||= units.find_by(shortname: 'g') || units.first
|
self.ref_unit ||= units.find_by(shortname: 'g') || units.first
|
||||||
self.group ||= :other
|
self.group ||= :other
|
||||||
|
self.hidden = false if self.hidden.nil?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
class Nutrient < ActiveRecord::Base
|
class Nutrient < ActiveRecord::Base
|
||||||
belongs_to :ingredient, inverse_of: :nutrients
|
belongs_to :ingredient, inverse_of: :nutrients, required: true
|
||||||
belongs_to :quantity
|
belongs_to :quantity, required: true
|
||||||
belongs_to :unit
|
belongs_to :unit, required: true
|
||||||
|
|
||||||
# disabled to avoid loop with Ingredient 'validates_associated :nutrients'
|
validates :quantity, uniqueness: {scope: :ingredient_id}
|
||||||
#validates :ingredient, presence: true, associated: true
|
validates :amount, numericality: {greater_thani_or_equal_to: 0.0}
|
||||||
validates :quantity, presence: true, associated: true, uniqueness: {scope: :ingredient_id}
|
|
||||||
validates :amount, numericality: {greater_than: 0}
|
|
||||||
validates :unit, presence: true, associated: true
|
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
if new_record?
|
if new_record?
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
class Quantity < ActiveRecord::Base
|
class Quantity < ActiveRecord::Base
|
||||||
acts_as_nested_set dependent: :nullify, scope: :project
|
|
||||||
|
|
||||||
enum domain: {
|
enum domain: {
|
||||||
diet: 0,
|
diet: 0,
|
||||||
measurement: 1,
|
measurement: 1,
|
||||||
exercise: 2
|
exercise: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
belongs_to :project
|
acts_as_nested_set dependent: :nullify, scope: :project
|
||||||
|
belongs_to :project, required: false
|
||||||
|
|
||||||
validates :project, associated: true
|
|
||||||
validates :name, presence: true, uniqueness: {scope: :project_id}
|
validates :name, presence: true, uniqueness: {scope: :project_id}
|
||||||
validates :domain, inclusion: {in: domains.keys}
|
validates :domain, inclusion: {in: domains.keys}
|
||||||
validates :parent, associated: true
|
|
||||||
validate if: -> { parent.present? } do
|
validate if: -> { parent.present? } do
|
||||||
errors.add(:parent, :parent_domain_mismatch) unless domain == parent.domain
|
errors.add(:parent, :parent_domain_mismatch) unless domain == parent.domain
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
class Unit < ActiveRecord::Base
|
class Unit < ActiveRecord::Base
|
||||||
belongs_to :project
|
belongs_to :project, required: true
|
||||||
|
|
||||||
validates :project, associated: true
|
|
||||||
validates :shortname, presence: true, uniqueness: {scope: :project_id}
|
validates :shortname, presence: true, uniqueness: {scope: :project_id}
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user