diff --git a/app/controllers/foods_controller.rb b/app/controllers/foods_controller.rb index bbacaff..efa84c2 100644 --- a/app/controllers/foods_controller.rb +++ b/app/controllers/foods_controller.rb @@ -9,7 +9,7 @@ class FoodsController < ApplicationController before_action :init_session_filters before_action :find_project_by_project_id, - only: [:index, :new, :create, :nutrients, :filter, :import] + only: [:index, :new, :create, :nutrients, :filter, :autocomplete, :import] before_action :find_quantity_by_quantity_id, only: [:toggle_column] before_action :find_food, only: [:edit, :update, :destroy, :toggle] before_action :authorize @@ -63,7 +63,7 @@ class FoodsController < ApplicationController end def toggle_column - @project.nutrient_columns.toggle!(@quantity) + @project.nutrient_exposures.toggle!(@quantity) prepare_nutrients end @@ -73,6 +73,10 @@ class FoodsController < ApplicationController render :index end + def autocomplete + @foods = @project.foods.where("name LIKE ?", "%#{params[:term]}%") + end + def import warnings = [] diff --git a/app/controllers/meals_controller.rb b/app/controllers/meals_controller.rb index cc81507..3bc20be 100644 --- a/app/controllers/meals_controller.rb +++ b/app/controllers/meals_controller.rb @@ -13,6 +13,17 @@ class MealsController < ApplicationController prepare_meals end + def new + @meal = @project.meals.new + @meal.ingredients.new + end + + def create + end + + def destroy + end + private def prepare_meals diff --git a/app/controllers/measurements_controller.rb b/app/controllers/measurements_controller.rb index 57882bc..be996d9 100644 --- a/app/controllers/measurements_controller.rb +++ b/app/controllers/measurements_controller.rb @@ -33,7 +33,7 @@ class MeasurementsController < ApplicationController @measurement.routine.project = @project @routine = @measurement.routine if @measurement.save - if @routine.columns.empty? + if @routine.exposures.empty? @routine.quantities << @measurement.readouts.map(&:quantity).first(6) end @@ -83,7 +83,7 @@ class MeasurementsController < ApplicationController end def toggle_column - @routine.columns.toggle!(@quantity) + @routine.exposures.toggle!(@quantity) prepare_readouts end diff --git a/app/controllers/quantities_controller.rb b/app/controllers/quantities_controller.rb index 85ca764..73af8be 100644 --- a/app/controllers/quantities_controller.rb +++ b/app/controllers/quantities_controller.rb @@ -122,6 +122,6 @@ class QuantitiesController < ApplicationController def prepare_quantities @quantities = @project.quantities.filter(@project, session[:q_filters]) - .includes(:columns, :formula, :parent) + .includes(:exposures, :formula, :parent) end end diff --git a/app/helpers/meals_helper.rb b/app/helpers/meals_helper.rb new file mode 100644 index 0000000..1a49f0a --- /dev/null +++ b/app/helpers/meals_helper.rb @@ -0,0 +1,5 @@ +module MealsHelper + def action_links(m) + delete_link(meal_path(m), {remote: true, data: {}}) if m.persisted? + end +end diff --git a/app/models/exposure.rb b/app/models/exposure.rb new file mode 100644 index 0000000..f56fabd --- /dev/null +++ b/app/models/exposure.rb @@ -0,0 +1,4 @@ +class Exposure < ActiveRecord::Base + belongs_to :view, polymorphic: true + belongs_to :quantity +end diff --git a/app/models/food.rb b/app/models/food.rb index f06490d..d3b044c 100644 --- a/app/models/food.rb +++ b/app/models/food.rb @@ -34,6 +34,9 @@ class Food < ActiveRecord::Base validates :ref_amount, numericality: {greater_than: 0} validates :group, inclusion: {in: groups.keys} + scope :visible, -> { where(hidden: false) } + scope :hidden, -> { where(hidden: true) } + after_initialize do if new_record? self.ref_amount ||= 100 diff --git a/app/models/ingredient.rb b/app/models/ingredient.rb new file mode 100644 index 0000000..ae9a171 --- /dev/null +++ b/app/models/ingredient.rb @@ -0,0 +1,8 @@ +class Ingredient < ActiveRecord::Base + belongs_to :composition, inverse_of: :ingredients, required: true + belongs_to :food, required: true + belongs_to :part_of, required: false + + validates :ready_ratio, numericality: {greater_than_or_equal_to: 0.0} + validates :amount, numericality: {greater_than_or_equal_to: 0.0} +end diff --git a/app/models/meal.rb b/app/models/meal.rb index 9e5bd52..9fea6ae 100644 --- a/app/models/meal.rb +++ b/app/models/meal.rb @@ -1,6 +1,19 @@ class Meal < ActiveRecord::Base belongs_to :project, required: true - has_many :ingredients, as: :composition, dependent: :destroy + has_many :ingredients, as: :composition, dependent: :destroy, validate: true has_many :foods, through: :ingredients + validates :ingredients, presence: true + accepts_nested_attributes_for :ingredients, allow_destroy: true, reject_if: proc { |attrs| + attrs['food_id'].blank? && attrs['amount'].blank? + } + # Ingredient food_id + part_of_id uniqueness validation. Cannot be effectively + # checked on Ingredient model level. + validate do + ingredients = self.ingredients.reject { |i| i.marked_for_destruction? } + .map { |i| [i.food_id, i.part_of_id] } + if ingredients.length != ingredients.uniq.length + errors.add(:ingredients, :duplicated_ingredient) + end + end end diff --git a/app/models/measurement_routine.rb b/app/models/measurement_routine.rb index 04f304a..44f05d7 100644 --- a/app/models/measurement_routine.rb +++ b/app/models/measurement_routine.rb @@ -3,9 +3,9 @@ class MeasurementRoutine < ActiveRecord::Base has_many :measurements, -> { order "taken_at DESC" }, inverse_of: :routine, foreign_key: 'routine_id', dependent: :restrict_with_error, extend: BodyTracking::ItemsWithQuantities - has_many :readout_columns, as: :column_view, dependent: :destroy, - class_name: 'QuantityColumn', extend: BodyTracking::TogglableColumns - has_many :quantities, -> { order "lft" }, through: :readout_columns + has_many :readout_exposures, as: :view, dependent: :destroy, + class_name: 'Exposure', extend: BodyTracking::TogglableColumns + has_many :quantities, -> { order "lft" }, through: :readout_exposures validates :name, presence: true, uniqueness: {scope: :project_id} end diff --git a/app/models/quantity.rb b/app/models/quantity.rb index 83d9041..1224801 100644 --- a/app/models/quantity.rb +++ b/app/models/quantity.rb @@ -9,7 +9,7 @@ class Quantity < ActiveRecord::Base belongs_to :project, required: false has_many :nutrients, dependent: :restrict_with_error has_many :readouts, dependent: :restrict_with_error - has_many :columns, dependent: :destroy + has_many :exposures, dependent: :destroy has_one :formula, inverse_of: :quantity, dependent: :destroy, validate: true accepts_nested_attributes_for :formula, allow_destroy: true, reject_if: proc { |attrs| diff --git a/app/models/quantity_column.rb b/app/models/quantity_column.rb deleted file mode 100644 index 3d0b620..0000000 --- a/app/models/quantity_column.rb +++ /dev/null @@ -1,4 +0,0 @@ -class QuantityColumn < ActiveRecord::Base - belongs_to :column_view, polymorphic: true - belongs_to :quantity -end diff --git a/app/views/foods/autocomplete.json.erb b/app/views/foods/autocomplete.json.erb new file mode 100644 index 0000000..3360e02 --- /dev/null +++ b/app/views/foods/autocomplete.json.erb @@ -0,0 +1 @@ +<%= raw @foods.map { |f| {id: f.id, label: f.name, value: f.name} }.to_json %> diff --git a/app/views/meals/_contextual.html.erb b/app/views/meals/_contextual.html.erb new file mode 100644 index 0000000..15f2a49 --- /dev/null +++ b/app/views/meals/_contextual.html.erb @@ -0,0 +1,4 @@ +<% if User.current.allowed_to?(:manage_common, @project) %> + <%= link_to t(".link_new_meal"), new_project_meal_path(@project), + {remote: true, class: 'icon icon-add'} %> +<% end %> diff --git a/app/views/meals/_form.html.erb b/app/views/meals/_form.html.erb new file mode 100644 index 0000000..b7e99f7 --- /dev/null +++ b/app/views/meals/_form.html.erb @@ -0,0 +1,79 @@ +<%= error_messages_for @meal %> + +
+ + <%= ff.hidden_field :id %> + <%= ff.text_field :food_id, {class: "autocomplete food-autocomplete", + style: "width: 80%;", + required: true, + label: (index > 0 ? '' : :field_ingredients)} %> + <%= ff.number_field :amount, {style: "width: 8%", step: :any, label: ''} %> + <%= i.food.ref_unit.shortname if i.food %> + <%= ff.hidden_field :_destroy %> + + |
+ + <%= link_to t(".button_delete_ingredient"), '#', + class: 'icon icon-del', + style: (@meal.ingredients.length > 1 ? "" : "display:none"), + onclick: "deleteIngredient(); return false;" %> + | +
+ <%= link_to t(".button_new_ingredient"), '#', class: 'icon icon-add', + onclick: 'newIngredient(); return false;' %> +
+
+ + <%= "#{t '.label_meal'}" %><%= index ? " ##{index+1}" : " (new)" %> + <%= ", #{m.eaten_at.time}" if m.eaten_at %> ++ |
+ <%= action_links(m) %> | +
<%= l(:label_no_data) %>
+<% end %> diff --git a/app/views/meals/_new_form.html.erb b/app/views/meals/_new_form.html.erb new file mode 100644 index 0000000..d1b5b77 --- /dev/null +++ b/app/views/meals/_new_form.html.erb @@ -0,0 +1,18 @@ ++ <%= submit_tag l(:button_create) %> + <%= link_to l(:button_cancel), "#", + onclick: '$("#new-meal").empty(); return false;' %> +
+