From 70b6e97b8739a76322bd6b31aa3fb23b3353aaf5 Mon Sep 17 00:00:00 2001 From: cryptogopher Date: Fri, 8 Nov 2019 23:42:41 +0100 Subject: [PATCH] Filtering ingredients by formula working --- app/controllers/ingredients_controller.rb | 33 ++++------------- app/models/ingredient.rb | 43 +++++++++++++++++++---- app/views/ingredients/_filters.html.erb | 2 +- lib/body_tracking/formula.rb | 13 ++++--- 4 files changed, 51 insertions(+), 40 deletions(-) diff --git a/app/controllers/ingredients_controller.rb b/app/controllers/ingredients_controller.rb index 0a1c0a3..a5068f0 100644 --- a/app/controllers/ingredients_controller.rb +++ b/app/controllers/ingredients_controller.rb @@ -196,48 +196,29 @@ class IngredientsController < ApplicationController end def prepare_ingredients - @ingredients = filter_ingredients.includes(:ref_unit, :source) + @ingredients = @project.ingredients.includes(:ref_unit, :source) + .filter(@project, session[:filters]) end def prepare_nutrients @quantities = @project.quantities.where(primary: true) - nutrients = filter_ingredients.compute_nutrients(@quantities) + ingredients, requested_n, extra_n = @project.ingredients + .filter(@project, session[:filters], @quantities) @nutrients = {} @extra_nutrients = {} - nutrients.each do |i, requested_n, extra_n| + ingredients.each_with_index do |i, index| @nutrients[i] = [] - requested_n.each do |q_name, value| + requested_n[index].each do |q_name, value| amount, unitname = value @nutrients[i] << [q_name, amount.nil? ? '-' : "#{amount} [#{unitname || '-'}]"] end @extra_nutrients[i] = [] - extra_n.each do |q_name, value| + extra_n[index].each do |q_name, value| amount, unitname = value @extra_nutrients[i] << [q_name, amount.nil? ? '-' : "#{amount} [#{unitname || '-'}]"] end end end - - def filter_ingredients - filters = session[:filters] || {} - ingredients = @project.ingredients - - if filters[:name].present? - ingredients = ingredients.where("name LIKE ?", "%#{filters[:name]}%") - end - - if filters[:visibility].present? - ingredients = ingredients.where(hidden: filters[:visibility] == "1" ? false : true) - end - - if filters[:nutrients].present? - formula = Formula.new(self.project, filters[:nutrients], comparison: true) - if formula.valid? - end - end - - ingredients - end end diff --git a/app/models/ingredient.rb b/app/models/ingredient.rb index 4d92c05..bbe66a8 100644 --- a/app/models/ingredient.rb +++ b/app/models/ingredient.rb @@ -1,4 +1,6 @@ class Ingredient < ActiveRecord::Base + include BodyTracking::Formula + enum group: { other: 0, meat: 1 @@ -35,9 +37,36 @@ class Ingredient < ActiveRecord::Base end end - def self.compute_nutrients(requested_q) + def self.filter(project, filters = {}, requested_q = []) + ingredients = all + + if filters[:name].present? + ingredients = ingredients.where("name LIKE ?", "%#{filters[:name]}%") + end + + if filters[:visibility].present? + ingredients = ingredients.where(hidden: filters[:visibility] == "1" ? false : true) + end + + formula_q = if filters[:nutrients].present? + formula = Formula.new(project, filters[:nutrients]) + if formula.valid? + project.quantities.new(name: '__internal_q', formula: filters[:nutrients]) + end + end + + if !requested_q.empty? || formula_q.present? + result = self.compute_nutrients(requested_q, formula_q) + requested_q.present? ? result : result[0] + else + ingredients + end + end + + def self.compute_nutrients(requested_q, filter_q = nil) ingredients = all unchecked_q = requested_q.map { |q| [q, nil] } + unchecked_q << [filter_q, nil] if filter_q nutrients = Hash.new { |h,k| h[k] = {} } Nutrient.where(ingredient: ingredients).includes(:quantity, :unit) @@ -90,10 +119,12 @@ class Ingredient < ActiveRecord::Base end all_q = nutrients.merge(completed_q) - ingredients.map do |i| - requested_n = requested_q.map { |q| [q.name, all_q[q.name][i.id]] } - extra_n = extra_q.map { |q_name| [q_name, all_q[q_name][i.id]] if all_q[q_name][i.id] } - [i, requested_n, extra_n.compact] - end + [ + filter_q ? ingredients.to_a.keep_if { |i| all_q[filter_q.name][i.id] } : ingredients, + ingredients.map { |i| requested_q.map { |q| [q.name, all_q[q.name][i.id]] } }, + ingredients.map do |i| + extra_q.map { |q_name| [q_name, all_q[q_name][i.id]] if all_q[q_name][i.id] } + end + ] end end diff --git a/app/views/ingredients/_filters.html.erb b/app/views/ingredients/_filters.html.erb index d554e58..1bfea56 100644 --- a/app/views/ingredients/_filters.html.erb +++ b/app/views/ingredients/_filters.html.erb @@ -15,7 +15,7 @@ <%= text_field_tag 'filters[nutrients]', session[:filters][:nutrients], placeholder: 'conditional expression including nutrients', size: 40, - style: "box-sizing:border-box; width:100%;", + :style => 'box-sizing:border-box; width:100%;', :onblur => '$("#filters_form").submit(); return false;' %> diff --git a/lib/body_tracking/formula.rb b/lib/body_tracking/formula.rb index 68e0a8d..8b7880c 100644 --- a/lib/body_tracking/formula.rb +++ b/lib/body_tracking/formula.rb @@ -4,10 +4,9 @@ module BodyTracking QUANTITY_TTYPES = [:on_ident, :on_tstring_content, :on_const] class Formula - def initialize(project, formula, options = {}) + def initialize(project, formula) @project = project @formula = formula - @options = options end def validate @@ -27,10 +26,10 @@ module BodyTracking identifiers << token when [:on_sp, :on_int, :on_rational, :on_float, :on_tstring_beg, :on_tstring_end, :on_lparen, :on_rparen].include?(ttype) - when :on_op == ttype && ['+', '-', '*', '/', '%', '**'].include?(token) - when :on_op == ttype && @options[:comparison] && - ['==', '!=', '>', '<', '>=', '<=', '<=>', '===', '..', '...', '?:', 'and', 'or', - 'not', '&&', '||', '!'].include?(token) + when :on_op == ttype && + ['+', '-', '*', '/', '%', '**', '==', '!=', '>', '<', '>=', '<=', '<=>', '===', + '..', '...', '?:', 'and', 'or', 'not', '&&', '||', '!'].include?(token) + when :on_kw == ttype && ['and', 'or', 'not'].include?(token) else errors << [:disallowed_token, {token: token, ttype: ttype, location: location}] end @@ -65,7 +64,7 @@ module BodyTracking def calculate(inputs) paramed_formula = Ripper.lex(@formula).map do |*, ttype, token| - QUANTITY_TTYPES.include?(ttype) ? "params['#{token}']" : token + QUANTITY_TTYPES.include?(ttype) ? "params['#{token}'].to_d" : token end.join inputs.map { |i, values| [i, get_binding(values).eval(paramed_formula)] } end