WIP: Targets configurable with Quantities
This commit is contained in:
parent
316005bf1f
commit
8b17b33603
@ -28,19 +28,19 @@ class BodyTrackersController < ApplicationController
|
||||
quantities_count = available_quantities.length
|
||||
defaults = Quantity.defaults
|
||||
Quantity.each_with_path(defaults) do |q, path|
|
||||
unless available_quantities.has_key?(path)
|
||||
attrs = q.attributes.except('id', 'project_id', 'parent_id', 'lft', 'rgt',
|
||||
'created_at', 'updated_at')
|
||||
if q.parent
|
||||
attrs['parent'] = available_quantities[path.rpartition('::').first]
|
||||
end
|
||||
if q.formula
|
||||
attrs['formula_attributes'] = q.formula.attributes
|
||||
.except('id', 'quantity_id', 'unit_id', 'created_at', 'updated_at')
|
||||
attrs['formula_attributes']['unit_id'] = available_units[q.formula.unit.shortname]
|
||||
end
|
||||
available_quantities[path] = @project.quantities.build(attrs)
|
||||
next if available_quantities.has_key?(path)
|
||||
|
||||
attrs = q.attributes.except('id', 'project_id', 'parent_id', 'lft', 'rgt', 'depth',
|
||||
'created_at', 'updated_at')
|
||||
if q.parent
|
||||
attrs['parent'] = available_quantities[path.rpartition('::').first]
|
||||
end
|
||||
if q.formula
|
||||
attrs['formula_attributes'] = q.formula.attributes
|
||||
.except('id', 'quantity_id', 'unit_id', 'created_at', 'updated_at')
|
||||
attrs['formula_attributes']['unit_id'] = available_units[q.formula.unit&.shortname]
|
||||
end
|
||||
available_quantities[path] = @project.quantities.build(attrs)
|
||||
end
|
||||
Quantity.transaction do
|
||||
failed_objects += available_quantities.values.reject { |o| o.persisted? || o.save }
|
||||
|
@ -6,6 +6,7 @@ class TargetsController < ApplicationController
|
||||
include Concerns::Finders
|
||||
|
||||
before_action :find_binding_goal_by_project_id, only: [:index, :new, :edit]
|
||||
before_action :find_project, only: [:subthresholds]
|
||||
before_action :find_project_by_project_id, only: [:create]
|
||||
before_action :find_quantity_by_quantity_id, only: [:toggle_exposure]
|
||||
#, if: ->{ params[:project_id].present? }
|
||||
@ -21,8 +22,9 @@ class TargetsController < ApplicationController
|
||||
|
||||
def new
|
||||
target = @goal.targets.new
|
||||
target.arity.times { target.thresholds.new }
|
||||
target.thresholds.new(quantity: Quantity.target.roots.last)
|
||||
@targets = [target]
|
||||
@effective_from = target.effective_from
|
||||
end
|
||||
|
||||
def create
|
||||
@ -75,6 +77,15 @@ class TargetsController < ApplicationController
|
||||
prepare_targets
|
||||
end
|
||||
|
||||
def subthresholds
|
||||
quantity_id = params[:goal][:targets_attributes]
|
||||
.last[:thresholds_attributes][:quantity_id]
|
||||
return if quantity_id.blank?
|
||||
|
||||
quantity = @project.quantities.find(quantity_id)
|
||||
@threshold = Threshold.new(quantity: quantity)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def goal_params
|
||||
|
@ -36,17 +36,18 @@ module BodyTrackersHelper
|
||||
options_for_select(options, disabled: 0)
|
||||
end
|
||||
|
||||
def quantity_options(domain = :all)
|
||||
def quantity_options(domain = :except_targets)
|
||||
Quantity.each_with_ancestors(@project.quantities.send(domain)).map do |ancestors|
|
||||
quantity = ancestors.last
|
||||
[
|
||||
raw("#{' ' * (ancestors.length-2)}#{quantity.name}"),
|
||||
raw(' '*(ancestors.length-2) + quantity.name),
|
||||
quantity.id,
|
||||
{'data-path' => ancestors[1..-2].reduce('::') { |m, q| "#{m}#{q.try(:name)}::" }}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: replace with collection_select and remove
|
||||
def unit_options
|
||||
@project.units.map do |u|
|
||||
[u.shortname, u.id]
|
||||
|
@ -1,8 +1,4 @@
|
||||
module TargetsHelper
|
||||
def condition_options
|
||||
Target::CONDITIONS
|
||||
end
|
||||
|
||||
def action_links(d)
|
||||
link_to(l(:button_reapply), reapply_project_targets_path(@project, d, @view_params),
|
||||
{remote: true, class: "icon icon-reload"}) +
|
||||
|
@ -1,6 +1,7 @@
|
||||
class Formula < ActiveRecord::Base
|
||||
include BodyTracking::FormulaBuilder
|
||||
|
||||
# NOTE: check if model_deps used and merge with quantity_deps if not
|
||||
attr_reader :parts, :quantity_deps, :model_deps
|
||||
|
||||
belongs_to :quantity, inverse_of: :formula, required: true
|
||||
@ -107,18 +108,23 @@ class Formula < ActiveRecord::Base
|
||||
end
|
||||
|
||||
quantities = []
|
||||
identifiers.reject! do |i|
|
||||
if q_paths.has_key?(i)
|
||||
q = q_paths[i]
|
||||
q.nil? ? errors << [:ambiguous_dependency, {identifier: i}] : quantities << q
|
||||
models = []
|
||||
identifiers.each do |i|
|
||||
case
|
||||
when q_paths.has_key?(i)
|
||||
if q_paths[i].nil?
|
||||
errors << [:ambiguous_dependency, {identifier: i}]
|
||||
else
|
||||
quantities << q_paths[i]
|
||||
end
|
||||
when quantity.target? && (i.casecmp('Value') == 0)
|
||||
when model = i.safe_constantize
|
||||
models << model
|
||||
else
|
||||
errors << [:unknown_dependency, {identifier: i}]
|
||||
end
|
||||
end
|
||||
|
||||
models = identifiers.map(&:safe_constantize).compact || []
|
||||
(identifiers - models.map(&:class_name)).each do |i|
|
||||
errors << [:unknown_dependency, {identifier: i}]
|
||||
end
|
||||
|
||||
@parts, @quantity_deps, @model_deps = parts, quantities, models if errors.empty?
|
||||
errors
|
||||
end
|
||||
|
@ -1,11 +1,13 @@
|
||||
class Goal < ActiveRecord::Base
|
||||
belongs_to :project, required: true
|
||||
has_many :targets, -> { order "effective_from DESC" }, inverse_of: :goal,
|
||||
has_many :targets, -> { order effective_from: :desc }, inverse_of: :goal,
|
||||
dependent: :destroy, extend: BodyTracking::ItemsWithQuantities
|
||||
has_many :target_exposures, as: :view, dependent: :destroy,
|
||||
class_name: 'Exposure', extend: BodyTracking::TogglableExposures
|
||||
has_many :quantities, -> { order "lft" }, through: :target_exposures
|
||||
|
||||
accepts_nested_attributes_for :targets, allow_destroy: true,
|
||||
reject_if: proc { |attrs| attrs['quantity_id'].blank? }
|
||||
validates :target_exposures, presence: true, unless: :is_binding?
|
||||
validates :is_binding, uniqueness: {scope: :project_id}, if: :is_binding?
|
||||
validates :name, presence: true, uniqueness: {scope: :project_id},
|
||||
|
@ -2,18 +2,21 @@ class Quantity < ActiveRecord::Base
|
||||
enum domain: {
|
||||
diet: 0,
|
||||
measurement: 1,
|
||||
exercise: 2
|
||||
exercise: 2,
|
||||
target: 3
|
||||
}
|
||||
|
||||
acts_as_nested_set dependent: :destroy, scope: :project
|
||||
belongs_to :project, inverse_of: :quantities, required: false
|
||||
has_many :nutrients, dependent: :restrict_with_error
|
||||
has_many :readouts, dependent: :restrict_with_error
|
||||
has_many :targets, dependent: :restrict_with_error
|
||||
has_many :thresholds, dependent: :restrict_with_error
|
||||
has_many :values, class_name: 'QuantityValue', dependent: :restrict_with_error
|
||||
has_many :exposures, dependent: :destroy
|
||||
|
||||
scope :defaults, -> { where(project: nil) }
|
||||
scope :except_targets, -> { where.not(domain: :target) }
|
||||
|
||||
has_one :formula, inverse_of: :quantity, dependent: :destroy, validate: true
|
||||
accepts_nested_attributes_for :formula, allow_destroy: true,
|
||||
|
@ -1,37 +1,37 @@
|
||||
class Target < ActiveRecord::Base
|
||||
CONDITIONS = ['<', '<=', '>', '>=', '==']
|
||||
|
||||
belongs_to :goal, inverse_of: :targets, required: true
|
||||
belongs_to :quantity, inverse_of: :targets, required: true
|
||||
belongs_to :item, polymorphic: true, inverse_of: :targets
|
||||
has_many :thresholds, as: :registry, inverse_of: :target, dependent: :destroy,
|
||||
validate: true
|
||||
has_many :thresholds, -> { joins(:quantity).order(:lft) },
|
||||
as: :registry, inverse_of: :target, dependent: :destroy, validate: true
|
||||
|
||||
validates :thresholds, presence: true
|
||||
accepts_nested_attributes_for :thresholds, allow_destroy: true,
|
||||
reject_if: proc { |attrs| attrs['quantity_id'].blank? && attrs['value'].blank? }
|
||||
validate do
|
||||
errors.add(:thresholds, :count_mismatch) unless thresholds.length == arity
|
||||
errors.add(:thresholds, :quantity_mismatch) if thresholds.to_a.uniq(&:quantity).length != 1
|
||||
quantities = thresholds.map(&:quantity)
|
||||
ancestors = quantities.max_by(:lft).self_and_ancestors
|
||||
errors.add(:thresholds, :count_mismatch) unless quantities.length == ancestors.length
|
||||
errors.add(:thresholds, :quantity_mismatch) unless quantities == ancestors
|
||||
end
|
||||
validates :condition, inclusion: {in: CONDITIONS}
|
||||
validates :scope, inclusion: {in: [:ingredient, :meal, :day],
|
||||
if: -> { thresholds.first.quantity.domain == :diet }}
|
||||
validates :scope, inclusion: {in: [:ingredient, :meal, :day], if: -> { quantity.diet? }}
|
||||
validates :effective_from, presence: {if: :is_binding?}, absence: {unless: :is_binding?}
|
||||
|
||||
after_initialize do
|
||||
if new_record?
|
||||
self.condition ||= CONDITIONS.first
|
||||
# Target should be only instantiated through Goal, so :is_binding? will be available
|
||||
self.effective_from ||= Date.current if is_binding?
|
||||
end
|
||||
end
|
||||
|
||||
delegate :is_binding?, to: :goal
|
||||
# NOTE: remove if not used in controller
|
||||
def arity
|
||||
BigDecimal.method(condition).arity
|
||||
thresholds.size
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{condition} #{thresholds.first.value} [#{thresholds.first.unit.shortname}]"
|
||||
thresholds.last.quantity.description %
|
||||
thresholds.map { |t| [t.quantity.name, "#{t.value} [#{t.unit.shortname}]"] }.to_h
|
||||
end
|
||||
end
|
||||
|
@ -1,21 +1,20 @@
|
||||
<label for="goal_id"><%= l(:field_goal) %><span class="required"> *</span></label>
|
||||
<%= select_tag :goal_id,
|
||||
options_from_collection_for_select(@project.goals, :id, :name, @goal.id),
|
||||
required: true, autocomplete: 'off',
|
||||
onchange: "var goal_id = $('#target_goal_attributes_id').val();
|
||||
$.ajax({
|
||||
url: '#{goal_path(id: :goal_id)}'.replace('goal_id', goal_id),
|
||||
<%= goal_f.collection_select :id, @project.goals, :id, :name,
|
||||
{include_blank: false, required: true, label: :field_goal},
|
||||
autocomplete: 'off',
|
||||
onchange: "$.ajax({
|
||||
url: '#{goal_path(id: :goal_id)}'
|
||||
.replace('goal_id', $('#target_goal_attributes_id').val()),
|
||||
dataType: 'script'
|
||||
});
|
||||
return false;" %>
|
||||
<%= link_to l(:button_add), '#',
|
||||
onclick: "var goal_id = $('#target_goal_attributes_id').val();
|
||||
$.ajax({
|
||||
url: '#{edit_goal_path(id: :goal_id)}'.replace('goal_id', goal_id),
|
||||
onclick: "$.ajax({
|
||||
url: '#{edit_goal_path(id: :goal_id)}'
|
||||
.replace('goal_id', $('#target_goal_attributes_id').val()),
|
||||
dataType: 'script'
|
||||
});
|
||||
return false;",
|
||||
class: 'icon icon-add' %>
|
||||
<% if @goal.description? %>
|
||||
<p style='white-space: pre-wrap;' ><%= @goal.description %></p>
|
||||
<% if goal_f.object.description? %>
|
||||
<p style='white-space: pre-wrap;' ><%= goal_f.object.description %></p>
|
||||
<% end %>
|
||||
|
@ -5,8 +5,7 @@
|
||||
{autocomplete: 'off',
|
||||
data: {remote: true,
|
||||
url: parents_project_quantities_path(@project),
|
||||
params: "form=#{f.options[:html][:id]}"}}
|
||||
%></p>
|
||||
params: "form=#{f.options[:html][:id]}"}} %></p>
|
||||
<p><%= f.select :parent_id, parent_options(@quantity.domain),
|
||||
{required: true, label: :field_parent_quantity, include_blank: t('.null_parent')} %></p>
|
||||
<p><%= f.text_field :name, size: 25, required: true %></p>
|
||||
|
@ -2,9 +2,10 @@
|
||||
|
||||
<div class="box">
|
||||
<div id='goal-form' class="tabular">
|
||||
<% if @goal.persisted? %>
|
||||
<p><%= render partial: 'goals/show_form' %></p>
|
||||
<p><%= f.date_field :effective_from, disabled: !@goal.is_binding? %></p>
|
||||
<% if goal_f.object.persisted? %>
|
||||
<p><%= render partial: 'goals/show_form', locals: {goal_f: goal_f} %></p>
|
||||
<p><%= goal_f.date_field :effective_from, value: @effective_from,
|
||||
disabled: !goal_f.object.is_binding? %></p>
|
||||
<% else %>
|
||||
<%= render partial: 'goals/form' %>
|
||||
<% end %>
|
||||
@ -13,34 +14,30 @@
|
||||
<hr style="width: 95%;">
|
||||
|
||||
<div class="tabular">
|
||||
<% @targets.each do |target| %>
|
||||
<p class="target">
|
||||
<%= f.fields_for 'targets', target, index: '' do |target_f| %>
|
||||
<%= target_f.hidden_field :id %>
|
||||
<%= target_f.hidden_field :_destroy %>
|
||||
<em class="info"><%= t ".choose_quantity" %></em>
|
||||
<% target.thresholds.each_with_index do |thr, index| %>
|
||||
<%= target_f.fields_for 'thresholds_attributes', thr, index: '' do |threshold_f| %>
|
||||
<%= threshold_f.hidden_field :id %>
|
||||
<% if index == 0 %>
|
||||
<%= threshold_f.select :quantity_id, quantity_options,
|
||||
{include_blank: true, required: true, label: :field_target},
|
||||
onchange: "showQuantityPath(event);" %>
|
||||
<%= target_f.select :condition, condition_options, required: true,
|
||||
label: '' %>
|
||||
<% end %>
|
||||
<%= threshold_f.number_field :value, {size: 8, step: :any, label: ''} %>
|
||||
<% if index == 0 %>
|
||||
<%= threshold_f.select :unit_id, unit_options, {label: ''} %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= link_to t(".button_delete_target"), '#', class: 'icon icon-del',
|
||||
style: (@targets.many? ? "" : "display:none"),
|
||||
onclick: "deleteTarget(); return false;" %>
|
||||
<p class="target">
|
||||
<%= goal_f.fields_for :targets, @targets, child_index: '' do |target_f| %>
|
||||
<%= target_f.hidden_field :_destroy %>
|
||||
<em class="info"><%= t ".choose_quantity" %></em>
|
||||
<%= target_f.select :quantity_id, quantity_options,
|
||||
{include_blank: true, required: true, label: :field_target},
|
||||
onchange: "showQuantityPath(event);" %>
|
||||
|
||||
<%= target_f.fields_for :thresholds do |threshold_f| %>
|
||||
<%= render partial: 'thresholds/form', locals: {threshold_f: threshold_f} %>
|
||||
<% end %>
|
||||
</p>
|
||||
<% last_quantity = target_f.object.thresholds.last.quantity %>
|
||||
<% unless last_quantity.leaf? %>
|
||||
<%= target_f.fields_for 'thresholds_attributes', Threshold.new do |threshold_f| %>
|
||||
<%= render partial: 'thresholds/form',
|
||||
locals: {threshold_f: threshold_f, parent: last_quantity} %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to t(".button_delete_target"), '#', class: 'icon icon-del',
|
||||
style: (@targets.many? ? "" : "display:none"),
|
||||
onclick: "deleteTarget(); return false;" %>
|
||||
<% end %>
|
||||
</p>
|
||||
<p>
|
||||
<%= link_to t(".button_new_target"), '#', class: 'icon icon-add',
|
||||
onclick: 'newTarget(); return false;' %>
|
||||
@ -50,10 +47,11 @@
|
||||
|
||||
<%= javascript_tag do %>
|
||||
function showQuantityPath(event) {
|
||||
$(event.target).prevAll('em').text($('option:selected', event.target).attr('data-path'));
|
||||
$(event.target).prevAll('em').text($('option:selected', event.target)
|
||||
.attr('data-path'));
|
||||
}
|
||||
$(document).ajaxComplete(function() {
|
||||
$('select[id$=__quantity_id]').trigger(jQuery.Event('change'));
|
||||
$('p.target select:first-child[id$=__quantity_id]').trigger(jQuery.Event('change'));
|
||||
})
|
||||
|
||||
function newTarget() {
|
||||
|
@ -1,11 +1,11 @@
|
||||
<h2><%= t ".heading_new_target" %></h2>
|
||||
|
||||
<%= labelled_form_for @targets,
|
||||
<%= labelled_form_for @goal,
|
||||
url: project_targets_path(@project, @view_params),
|
||||
remote: true,
|
||||
html: {id: 'new-target-form', name: 'new-target-form'} do |f| %>
|
||||
html: {id: 'new-target-form', name: 'new-target-form'} do |goal_f| %>
|
||||
|
||||
<%= render partial: 'targets/form', locals: {f: f} %>
|
||||
<%= render partial: 'targets/form', locals: {goal_f: goal_f} %>
|
||||
|
||||
<div class="tabular">
|
||||
<p>
|
||||
|
5
app/views/targets/subthresholds.js.erb
Normal file
5
app/views/targets/subthresholds.js.erb
Normal file
@ -0,0 +1,5 @@
|
||||
<% if @threshold %>
|
||||
$('#targets').html('<%= j render partial: 'targets/index' %>');
|
||||
<% else %>
|
||||
$(event.target).empty();
|
||||
<% end %>
|
15
app/views/thresholds/_form.html.erb
Normal file
15
app/views/thresholds/_form.html.erb
Normal file
@ -0,0 +1,15 @@
|
||||
<% threshold_q = threshold_f.object.quantity %>
|
||||
<% parent_id = defined?(parent) ? parent.id : threshold_q.parent_id %>
|
||||
<%= threshold_f.collection_select :quantity_id,
|
||||
@project.quantities.target.children_of(parent_id),
|
||||
:id, :name,
|
||||
{prompt: parent_id.nil? ? false : '.', required: true, no_label: true},
|
||||
{autocomplete: 'off', data: {remote: true,
|
||||
url: subthresholds_quantity_path(@project)}} %>
|
||||
|
||||
<% unless threshold_q.nil? %>
|
||||
<%= threshold_f.hidden_field :_destroy %>
|
||||
<%= threshold_f.number_field :value, {size: 8, step: :any, no_label: true} %>
|
||||
<%= threshold_f.collection_select :unit_id, @project.units, :id, :shortname,
|
||||
{no_label: true} %>
|
||||
<% end %>
|
@ -176,9 +176,10 @@ en:
|
||||
link_new_quantity: 'New quantity'
|
||||
form:
|
||||
domains:
|
||||
diet: 'diet'
|
||||
measurement: 'measurement'
|
||||
exercise: 'exercise'
|
||||
diet: 'diet'
|
||||
target: 'target'
|
||||
null_parent: '- none -'
|
||||
formula_placeholder: 'provide if value of quantity has to be computed in terms of
|
||||
other quantities'
|
||||
|
@ -9,18 +9,20 @@ resources :projects, shallow: true do
|
||||
end
|
||||
resources :goals, only: [:show, :edit] do
|
||||
member do
|
||||
post 'toggle_exposure', to: 'targets#toggle_exposure'
|
||||
post 'toggle_exposure', controller: :targets
|
||||
end
|
||||
end
|
||||
resources :targets, except: [:show, :edit, :update] do
|
||||
collection do
|
||||
get 'edit/:date', to: 'targets#edit', as: :edit
|
||||
get 'edit/:date', action: :edit, as: :edit
|
||||
patch :update
|
||||
post 'reapply/:date', to: 'targets#reapply', as: :reapply
|
||||
post 'reapply/:date', action: :reapply, as: :reapply
|
||||
end
|
||||
end
|
||||
resources :ingredients, only: [] do
|
||||
post 'adjust/:adjustment', to: 'meals#adjust', as: :adjust, on: :member
|
||||
member do
|
||||
post 'adjust/:adjustment', controller: :meals, action: :adjust, as: :adjust
|
||||
end
|
||||
end
|
||||
resources :meals, except: [:show] do
|
||||
member do
|
||||
@ -34,8 +36,8 @@ resources :projects, shallow: true do
|
||||
end
|
||||
resources :measurement_routines, only: [:show, :edit] do
|
||||
member do
|
||||
get 'readouts', to: 'measurements#readouts'
|
||||
post 'toggle_exposure', to: 'measurements#toggle_exposure'
|
||||
get 'readouts', controller: :measurements
|
||||
post 'toggle_exposure', controller: :measurements
|
||||
end
|
||||
end
|
||||
resources :measurements, except: [:show] do
|
||||
@ -47,7 +49,9 @@ resources :projects, shallow: true do
|
||||
end
|
||||
end
|
||||
resources :foods, except: [:show] do
|
||||
post 'toggle', on: :member
|
||||
member do
|
||||
post 'toggle'
|
||||
end
|
||||
collection do
|
||||
get 'nutrients'
|
||||
post 'toggle_exposure'
|
||||
@ -60,8 +64,9 @@ resources :projects, shallow: true do
|
||||
resources :quantities, except: [:show] do
|
||||
member do
|
||||
get 'new_child'
|
||||
get 'subthresholds', controller: :targets
|
||||
post 'create_child'
|
||||
post 'move/:direction', to: 'quantities#move', as: :move
|
||||
post 'move/:direction', action: :move, as: :move
|
||||
end
|
||||
collection do
|
||||
get 'parents'
|
||||
|
@ -10,6 +10,8 @@ class CreateSchema <
|
||||
t.references :parent
|
||||
t.integer :lft, null: false, index: true
|
||||
t.integer :rgt, null: false, index: true
|
||||
# TODO: remove depth (seems to be replaceable by lft)
|
||||
t.integer :depth, null: false, default: 0
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
@ -105,8 +107,8 @@ class CreateSchema <
|
||||
|
||||
create_table :targets do |t|
|
||||
t.references :goal
|
||||
t.references :quantity
|
||||
t.references :item, polymorphic: true
|
||||
t.string :condition
|
||||
t.string :scope
|
||||
t.date :effective_from
|
||||
t.timestamps null: false
|
||||
|
19
db/seeds.rb
19
db/seeds.rb
@ -168,6 +168,18 @@ b_ac = Quantity.create name: "RM", domain: :measurement, p
|
||||
b_ad = Quantity.create name: "VF", domain: :measurement, parent: b_a,
|
||||
description: "Visceral fat"
|
||||
|
||||
# -> Target conditions
|
||||
t_a = Quantity.create name: "below", domain: :target, parent: nil,
|
||||
description: "Upper bound"
|
||||
t_b = Quantity.create name: "above", domain: :target, parent: nil,
|
||||
description: "Lower bound"
|
||||
t_ba = Quantity.create name: "and below", domain: :target, parent: t_b,
|
||||
description: "Range"
|
||||
t_c = Quantity.create name: "equal", domain: :target, parent: nil,
|
||||
description: "Exact value"
|
||||
t_ca = Quantity.create name: "with accuracy of", domain: :target, parent: t_c,
|
||||
description: "Point range"
|
||||
|
||||
# Formulas go at the and to make sure dependencies exist
|
||||
e_aa.create_formula zero_nil: true, unit: u_b,
|
||||
code: "4*Proteins + 9*Fats + 4*Carbs + 2*Fibre"
|
||||
@ -189,6 +201,13 @@ e_aea.create_formula zero_nil: true, unit: u_c,
|
||||
b_aaa.create_formula zero_nil: true, unit: u_ac,
|
||||
code: "'% fat' * Weight"
|
||||
|
||||
t_a.create_formula zero_nil: false, code: "value <= below"
|
||||
t_b.create_formula zero_nil: false, code: "value >= above"
|
||||
t_ba.create_formula zero_nil: false, code: "(value >= above) && (value <= 'and below')"
|
||||
t_c.create_formula zero_nil: false, code: "value == equal"
|
||||
t_ca.create_formula zero_nil: false, code: "(value >= (equal - 'with accuracy of')) && " \
|
||||
"(value <= (equal + 'with accuracy of'))"
|
||||
|
||||
# Sources
|
||||
s_a = Source.create name: "nutrition label",
|
||||
description: "nutrition facts taken from package nutrition label"
|
||||
|
6
init.rb
6
init.rb
@ -28,7 +28,8 @@ Redmine::Plugin.register :body_tracking do
|
||||
permission :manage_body_trackers, {
|
||||
body_trackers: [:defaults],
|
||||
goals: [:edit],
|
||||
targets: [:new, :create, :edit, :update, :destroy, :reapply, :toggle_exposure],
|
||||
targets: [:new, :create, :edit, :update, :destroy, :reapply, :toggle_exposure,
|
||||
:subthresholds],
|
||||
meals: [:new, :create, :edit, :update, :destroy, :edit_notes, :update_notes,
|
||||
:toggle_eaten, :toggle_exposure, :adjust],
|
||||
measurement_routines: [:edit],
|
||||
@ -36,7 +37,8 @@ Redmine::Plugin.register :body_tracking do
|
||||
foods: [:new, :create, :edit, :update, :destroy, :toggle, :toggle_exposure,
|
||||
:import],
|
||||
sources: [:create, :destroy],
|
||||
quantities: [:new, :create, :edit, :update, :destroy, :move, :new_child, :create_child],
|
||||
quantities: [:new, :create, :edit, :update, :destroy, :move, :new_child,
|
||||
:create_child],
|
||||
units: [:create, :destroy],
|
||||
}, require: :loggedin
|
||||
end
|
||||
|
14
test/fixtures/formulas.yml
vendored
14
test/fixtures/formulas.yml
vendored
@ -0,0 +1,14 @@
|
||||
formulas_01:
|
||||
quantity: quantities_target_above
|
||||
zero_nil: false
|
||||
code: value >= above
|
||||
|
||||
formulas_02:
|
||||
quantity: quantities_target_range
|
||||
zero_nil: false
|
||||
code: (value >= above) && (value <= 'and below')
|
||||
|
||||
formulas_03:
|
||||
quantity: quantities_target_equal
|
||||
zero_nil: false
|
||||
code: value == equal
|
40
test/fixtures/quantities.yml
vendored
40
test/fixtures/quantities.yml
vendored
@ -1,17 +1,49 @@
|
||||
quantities_energy:
|
||||
DEFAULTS: &DEFAULTS
|
||||
project_id: 1
|
||||
domain: diet
|
||||
parent: null
|
||||
|
||||
quantities_energy:
|
||||
<<: *DEFAULTS
|
||||
domain: diet
|
||||
lft: 1
|
||||
rgt: 2
|
||||
depth: 0
|
||||
name: Energy
|
||||
description: Total energy
|
||||
|
||||
quantities_proteins:
|
||||
project_id: 1
|
||||
<<: *DEFAULTS
|
||||
domain: diet
|
||||
parent: null
|
||||
lft: 3
|
||||
rgt: 4
|
||||
depth: 0
|
||||
name: Proteins
|
||||
description: Total amount of proteins
|
||||
|
||||
quantities_target_above:
|
||||
<<: *DEFAULTS
|
||||
domain: target
|
||||
lft: 5
|
||||
rgt: 8
|
||||
depth: 0
|
||||
name: above
|
||||
description: Lower bound
|
||||
|
||||
quantities_target_range:
|
||||
<<: *DEFAULTS
|
||||
parent: quantities_target_above
|
||||
domain: target
|
||||
lft: 6
|
||||
rgt: 7
|
||||
depth: 1
|
||||
name: and below
|
||||
description: Range
|
||||
|
||||
quantities_target_equal:
|
||||
<<: *DEFAULTS
|
||||
domain: target
|
||||
lft: 9
|
||||
rgt: 10
|
||||
depth: 0
|
||||
name: equal
|
||||
description: Exact value
|
||||
|
2
test/fixtures/quantity_values.yml
vendored
2
test/fixtures/quantity_values.yml
vendored
@ -2,6 +2,6 @@ quantity_values_001:
|
||||
type: Threshold
|
||||
registry_type: Target
|
||||
registry: targets_01
|
||||
quantity: quantities_energy
|
||||
quantity: quantities_target_equal
|
||||
value: 2500
|
||||
unit: units_kcal
|
||||
|
2
test/fixtures/targets.yml
vendored
2
test/fixtures/targets.yml
vendored
@ -1,4 +1,4 @@
|
||||
targets_01:
|
||||
goal: goals_binding
|
||||
condition: '=='
|
||||
quantity: quantities_energy
|
||||
effective_from: <%= 1.week.ago.to_s(:db) %>
|
||||
|
Reference in New Issue
Block a user