From 3ebc2f58d0bb20ef5fb5009e9a0b90231ac95ad4 Mon Sep 17 00:00:00 2001 From: cryptogopher Date: Sun, 22 Sep 2019 20:26:07 +0200 Subject: [PATCH] Finished CSV import Fixed problem with excessive validation --- Gemfile | 3 ++- app/controllers/ingredients_controller.rb | 25 +++++++++++++++-------- app/models/ingredient.rb | 13 ++++++------ app/models/nutrient.rb | 13 +++++------- app/models/quantity.rb | 7 ++----- app/models/unit.rb | 3 +-- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/Gemfile b/Gemfile index d52745d..0c42c44 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,8 @@ gem 'awesome_nested_set' group :development do - gem "web-console" + gem 'web-console' + gem 'active_record_query_trace' end group :development, :test do diff --git a/app/controllers/ingredients_controller.rb b/app/controllers/ingredients_controller.rb index ce43d42..fac62f7 100644 --- a/app/controllers/ingredients_controller.rb +++ b/app/controllers/ingredients_controller.rb @@ -9,7 +9,7 @@ class IngredientsController < ApplicationController @ingredient = Ingredient.new(project: @project) # passing attr for after_initialize @ingredient.nutrients.new(ingredient: @ingredient) - @ingredients = @project.ingredients + @ingredients = @project.ingredients.includes(:ref_unit) end def create @@ -18,7 +18,7 @@ class IngredientsController < ApplicationController flash[:notice] = 'Created new ingredient' redirect_to project_ingredients_url(@project) else - @ingredients = @project.ingredients + @ingredients = @project.ingredients.includes(:ref_unit) render :index end end @@ -37,7 +37,7 @@ class IngredientsController < ApplicationController if params.has_key?(:file) quantities = @project.quantities.map { |q| [q.name, q] }.to_h units = @project.units.map { |u| [u.shortname, u] }.to_h - ingredients = [] + ingredients_params = [] column_units = {} 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') warnings << "Line 1: required 'Name' column is missing" if line == 2 end - i = { + i_params = { name: r.delete('Name'), ref_amount: 100.0, ref_unit: units['g'], @@ -88,25 +88,34 @@ class IngredientsController < ApplicationController next if quantities[quantity].blank? if quantity == 'Reference' - i.update({ + i_params.update({ ref_amount: amount.to_d, ref_unit: unit }) else - i[:nutrients_attributes] << { + i_params[:nutrients_attributes] << { quantity: quantities[quantity], amount: amount.to_d, unit: unit } end end - ingredients << i + + ingredients_params << i_params end else warnings << 'No file selected' 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:
" \ + " #{skipped_desc.join('
').truncate(1024)}" + else warnings.unshift("Problems encountered during import - fix and try again:") flash[:warning] = warnings.join("
").truncate(1024, omission: '...(and other)') end diff --git a/app/models/ingredient.rb b/app/models/ingredient.rb index d65b3da..0a25f40 100644 --- a/app/models/ingredient.rb +++ b/app/models/ingredient.rb @@ -4,26 +4,24 @@ class Ingredient < ActiveRecord::Base meat: 1 } - belongs_to :project - belongs_to :ref_unit, class_name: 'Unit' + belongs_to :project, required: true + 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| attrs['quantity_id'].blank? && attrs['amount'].blank? } - validates_associated :nutrients # Nutrient quantity_id uniqueness check for nested attributes - validate on: :create do + validate do quantities = self.nutrients.map { |n| n.quantity_id } if quantities.length != quantities.uniq.length errors.add(:nutrients, :duplicated_quantity) end end - validates :project, associated: true validates :name, presence: true, uniqueness: {scope: :project_id} validates :ref_amount, numericality: {greater_than: 0} - validates :ref_unit, presence: true, associated: true validates :group, inclusion: {in: groups.keys} after_initialize do @@ -32,6 +30,7 @@ class Ingredient < ActiveRecord::Base units = self.project.units self.ref_unit ||= units.find_by(shortname: 'g') || units.first self.group ||= :other + self.hidden = false if self.hidden.nil? end end diff --git a/app/models/nutrient.rb b/app/models/nutrient.rb index df60400..1abc0b1 100644 --- a/app/models/nutrient.rb +++ b/app/models/nutrient.rb @@ -1,13 +1,10 @@ class Nutrient < ActiveRecord::Base - belongs_to :ingredient, inverse_of: :nutrients - belongs_to :quantity - belongs_to :unit + belongs_to :ingredient, inverse_of: :nutrients, required: true + belongs_to :quantity, required: true + belongs_to :unit, required: true - # disabled to avoid loop with Ingredient 'validates_associated :nutrients' - #validates :ingredient, presence: true, associated: true - validates :quantity, presence: true, associated: true, uniqueness: {scope: :ingredient_id} - validates :amount, numericality: {greater_than: 0} - validates :unit, presence: true, associated: true + validates :quantity, uniqueness: {scope: :ingredient_id} + validates :amount, numericality: {greater_thani_or_equal_to: 0.0} after_initialize do if new_record? diff --git a/app/models/quantity.rb b/app/models/quantity.rb index 3dedc3f..c065821 100644 --- a/app/models/quantity.rb +++ b/app/models/quantity.rb @@ -1,18 +1,15 @@ class Quantity < ActiveRecord::Base - acts_as_nested_set dependent: :nullify, scope: :project - enum domain: { diet: 0, measurement: 1, 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 :domain, inclusion: {in: domains.keys} - validates :parent, associated: true validate if: -> { parent.present? } do errors.add(:parent, :parent_domain_mismatch) unless domain == parent.domain end diff --git a/app/models/unit.rb b/app/models/unit.rb index c446468..7aeff43 100644 --- a/app/models/unit.rb +++ b/app/models/unit.rb @@ -1,6 +1,5 @@ class Unit < ActiveRecord::Base - belongs_to :project + belongs_to :project, required: true - validates :project, associated: true validates :shortname, presence: true, uniqueness: {scope: :project_id} end