From 7f7a81d49aee0f08625705ab4f4361770913457d Mon Sep 17 00:00:00 2001 From: Dan Elbert Date: Sun, 24 Jan 2016 19:06:26 -0600 Subject: [PATCH] ingredient linking --- app/assets/javascripts/ingredients.js | 43 ++++++++++++ app/assets/javascripts/recipes.js | 57 ++++++++------- app/assets/javascripts/typeahead_selector.js | 70 +++++++++++++++++++ app/controllers/ingredients_controller.rb | 4 ++ app/models/ingredient.rb | 7 ++ app/views/ingredients/_form.html.erb | 70 ++++++++++++++++++- .../usda_food_search.json.jbuilder | 7 ++ config/routes.rb | 1 + ...20160124231837_add_fields_to_ingredient.rb | 15 ++++ db/schema.rb | 16 +++-- lib/tasks/usda.rake | 0 11 files changed, 258 insertions(+), 32 deletions(-) create mode 100644 app/assets/javascripts/ingredients.js create mode 100644 app/assets/javascripts/typeahead_selector.js create mode 100644 app/views/ingredients/usda_food_search.json.jbuilder create mode 100644 db/migrate/20160124231837_add_fields_to_ingredient.rb create mode 100644 lib/tasks/usda.rake diff --git a/app/assets/javascripts/ingredients.js b/app/assets/javascripts/ingredients.js new file mode 100644 index 0000000..aa49ca3 --- /dev/null +++ b/app/assets/javascripts/ingredients.js @@ -0,0 +1,43 @@ +(function($) { + + var usdaFoodSearchEngine = new Bloodhound({ + initialize: false, + datumTokenizer: function(datum) { + return Bloodhound.tokenizers.whitespace(datum.name); + }, + queryTokenizer: Bloodhound.tokenizers.whitespace, + identify: function(datum) { return datum.ndbn; }, + sorter: function(a, b) { + if (a.name < b.name) { + return -1; + } else if (b.name < a.name) { + return 1; + } else { + return 0; + } + }, + remote: { + url: '/ingredients/usda_food_search.json?query=%QUERY', + wildcard: '%QUERY' + } + }); + + $(document).on("ready page:load", function() { + var $ingredientForm = $("#ingredient_form"); + + if ($ingredientForm.length) { + usdaFoodSearchEngine.initialize(false); + } + + $ingredientForm.find(".ndbn_typeahead").typeahead_selector({ + + },{ + name: 'usdaFoods', + source: usdaFoodSearchEngine, + display: function(datum) { + return datum.name; + } + }); + }); + +})(jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/recipes.js b/app/assets/javascripts/recipes.js index f529e56..99394d9 100644 --- a/app/assets/javascripts/recipes.js +++ b/app/assets/javascripts/recipes.js @@ -1,5 +1,31 @@ (function($) { + var ingredientSearchEngine = new Bloodhound({ + initialize: false, + datumTokenizer: function(datum) { + return Bloodhound.tokenizers.whitespace(datum.name); + }, + queryTokenizer: Bloodhound.tokenizers.whitespace, + identify: function(datum) { return datum.id; }, + sorter: function(a, b) { + if (a.name < b.name) { + return -1; + } else if (b.name < a.name) { + return 1; + } else { + return 0; + } + }, + prefetch: { + url: '/ingredients/prefetch.json', + cache: false + }, + remote: { + url: '/ingredients/search.json?query=%QUERY', + wildcard: '%QUERY' + } + }); + function reorder($container) { $container.find("div.nested-fields").each(function(idx, editor) { var $editor = $(editor); @@ -104,33 +130,13 @@ $(document).on("ready page:load", function() { - var ingredientSearchEngine = new Bloodhound({ - datumTokenizer: function(datum) { - return Bloodhound.tokenizers.whitespace(datum.name); - }, - queryTokenizer: Bloodhound.tokenizers.whitespace, - identify: function(datum) { return datum.id; }, - sorter: function(a, b) { - if (a.name < b.name) { - return -1; - } else if (b.name < a.name) { - return 1; - } else { - return 0; - } - }, - prefetch: { - url: '/ingredients/prefetch.json', - cache: false - }, - remote: { - url: '/ingredients/search.json?query=%QUERY', - wildcard: '%QUERY' - } - }); - + var $ingredientList = $("#ingredient-list"); var $stepList = $("#step-list"); + if ($ingredientList.length) { + ingredientSearchEngine.initialize(false); + } + initializeStepEditor($stepList); $stepList @@ -147,7 +153,6 @@ $span.html($this.val()); }); - var $ingredientList = $("#ingredient-list"); initializeIngredientEditor($ingredientList, ingredientSearchEngine); diff --git a/app/assets/javascripts/typeahead_selector.js b/app/assets/javascripts/typeahead_selector.js new file mode 100644 index 0000000..16a83c4 --- /dev/null +++ b/app/assets/javascripts/typeahead_selector.js @@ -0,0 +1,70 @@ +(function($) { + + var pluginName = "typeahead_selector"; + + var defaultOptions = { + }; + + var methods = { + initialize: function (opts, sources) { + + return this.each(function() { + var options = $.extend({}, defaultOptions, opts); + var $this = $(this); + $this.typeahead(opts, sources); + + $this + .on("typeahead:change", function(evt, value) { + privateMethods.change($this, value); + }) + .on("typeahead:select", function(evt, value) { + privateMethods.select($this, value); + }) + .on("typeahead:autocomplete", function(evt, value) { + privateMethods.autocomplete($this, value); + }); + }); + }, + + val: function() { + if (this.length) { + return $(this[0]).data('typeahead_selected'); + } else { + return null; + } + } + }; + + var privateMethods = { + change: function($this, value) { + var item = $this.data('typeahead_pending'); + if (item) { + $this.data('typeahead_pending', null); + $this.data('typeahead_selected', item); + $this.trigger("typeahead_selector:selected", item); + } else { + $this.data('typeahead_selected', null); + $this.trigger("typeahead_selector:invalid", value); + } + }, + + select: function($this, item) { + $this.data('typeahead_pending', item); + }, + + autocomplete: function($this, item) { + $this.data('typeahead_pending', item); + } + }; + + $.fn[pluginName] = function (method) { + if (methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (typeof method === 'object' || ! method) { + return methods.initialize.apply(this, arguments); + } else { + $.error('Method ' + method + ' does not exist on jQuery.' + pluginName); + } + }; + +})(jQuery); \ No newline at end of file diff --git a/app/controllers/ingredients_controller.rb b/app/controllers/ingredients_controller.rb index 2afab75..f42198f 100644 --- a/app/controllers/ingredients_controller.rb +++ b/app/controllers/ingredients_controller.rb @@ -80,6 +80,10 @@ class IngredientsController < ApplicationController end end + def usda_food_search + @foods = UsdaFood.where("short_description LIKE ?", "%#{params[:query]}%").limit(50) + end + private # Use callbacks to share common setup or constraints between actions. def set_ingredient diff --git a/app/models/ingredient.rb b/app/models/ingredient.rb index ff2852e..00121a6 100644 --- a/app/models/ingredient.rb +++ b/app/models/ingredient.rb @@ -5,6 +5,13 @@ class Ingredient < ActiveRecord::Base def set_usda_food(food) self.ndbn = food.ndbn + self.water = food.water + self.protein = food.protein + self.lipids = food.lipid + self.ash = food.ash + self.kcal = food.kcal + self.fiber = food.fiber + self.sugar = food.sugar self.density = calculate_density(food.gram_weight_1, food.gram_weight_desc_1) || calculate_density(food.gram_weight_2, food.gram_weight_desc_2) end diff --git a/app/views/ingredients/_form.html.erb b/app/views/ingredients/_form.html.erb index 9235d50..4632122 100644 --- a/app/views/ingredients/_form.html.erb +++ b/app/views/ingredients/_form.html.erb @@ -1,6 +1,6 @@ -<%= form_for(@ingredient) do |f| %> +<%= form_for(@ingredient, html: {id: 'ingredient_form'}) do |f| %> <%= render partial: 'shared/error_list', locals: {model: @ingredient} %> @@ -9,6 +9,47 @@ <%= f.text_field :name, class: 'form-control', autofocus: true %> +
+ <%= f.label :ndbn, "Nutrient Databank Number", class: 'control-label' %> + +
+ +
+ Per 100 Grams + +
+ <%= f.label :water, "Grams of Water", class: 'control-label' %> + <%= f.text_field :water, class: 'form-control' %> +
+ +
+ <%= f.label :protein, "Grams of Protein", class: 'control-label' %> + <%= f.text_field :protein, class: 'form-control' %> +
+ +
+ <%= f.label :lipids, "Grams of Fat", class: 'control-label' %> + <%= f.text_field :lipids, class: 'form-control' %> +
+ +
+ <%= f.label :kcal, "Calories", class: 'control-label' %> + <%= f.text_field :kcal, class: 'form-control' %> +
+ +
+ <%= f.label :fiber, "Grams of Fiber", class: 'control-label' %> + <%= f.text_field :fiber, class: 'form-control' %> +
+ +
+ <%= f.label :sugar, "Grams of Sugar", class: 'control-label' %> + <%= f.text_field :sugar, class: 'form-control' %> +
+
+
<%= f.label :density, class: 'control-label' %> <%= f.text_field :density, class: 'form-control' %> @@ -22,5 +63,30 @@
<%= f.submit class: 'btn btn-primary' %>
-<% end %> + + +<% end %> diff --git a/app/views/ingredients/usda_food_search.json.jbuilder b/app/views/ingredients/usda_food_search.json.jbuilder new file mode 100644 index 0000000..0c4456f --- /dev/null +++ b/app/views/ingredients/usda_food_search.json.jbuilder @@ -0,0 +1,7 @@ + +json.array! @foods do |f| + + json.extract! f, :ndbn + json.name f.short_description + +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index f20c78a..74c902c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,6 +12,7 @@ Rails.application.routes.draw do get :search get :prefetch get :convert + get :usda_food_search end end end diff --git a/db/migrate/20160124231837_add_fields_to_ingredient.rb b/db/migrate/20160124231837_add_fields_to_ingredient.rb new file mode 100644 index 0000000..afd63ee --- /dev/null +++ b/db/migrate/20160124231837_add_fields_to_ingredient.rb @@ -0,0 +1,15 @@ +class AddFieldsToIngredient < ActiveRecord::Migration + def change + change_table :ingredients do |t| + t.decimal :water, precision: 10, scale: 2 + t.decimal :protein, precision: 10, scale: 2 + t.decimal :lipids, precision: 10, scale: 2 + t.decimal :ash, precision: 10, scale: 2 + t.decimal :carbohydrates, precision: 10, scale: 2 + t.integer :kcal + t.decimal :fiber, precision: 10, scale: 1 + t.decimal :sugar, precision: 10, scale: 2 + + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 8bb240a..3410d98 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,15 +11,23 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160124222409) do +ActiveRecord::Schema.define(version: 20160124231837) do create_table "ingredients", force: :cascade do |t| t.string "name" t.string "density" t.text "notes" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "ndbn", limit: 5 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "ndbn", limit: 5 + t.decimal "water", precision: 10, scale: 2 + t.decimal "protein", precision: 10, scale: 2 + t.decimal "lipids", precision: 10, scale: 2 + t.decimal "ash", precision: 10, scale: 2 + t.decimal "carbohydrates", precision: 10, scale: 2 + t.integer "kcal" + t.decimal "fiber", precision: 10, scale: 1 + t.decimal "sugar", precision: 10, scale: 2 end create_table "recipe_ingredients", force: :cascade do |t| diff --git a/lib/tasks/usda.rake b/lib/tasks/usda.rake new file mode 100644 index 0000000..e69de29