Added MeasurementRoutine as a nested Measurement model
Updated ItemsWithQuantities to work with MeasurementRoutine Replaced ColumnViews HABTM with polymorphic HMT Added Measurement notes Added destroy restrictions on Quantity Replaced BodyTrackingPluginController with Finders concern Removed 'body_trackers' prefix from paths Unified styling for textarea
This commit is contained in:
parent
e7a33c684f
commit
18419f1aeb
@ -1,4 +1,7 @@
|
|||||||
class BodyTrackersController < BodyTrackingPluginController
|
class BodyTrackersController < ApplicationController
|
||||||
|
layout 'body_tracking'
|
||||||
|
menu_item :body_trackers
|
||||||
|
|
||||||
before_action :find_project_by_project_id, only: [:index, :defaults]
|
before_action :find_project_by_project_id, only: [:index, :defaults]
|
||||||
before_action :authorize
|
before_action :authorize
|
||||||
|
|
||||||
@ -43,10 +46,8 @@ class BodyTrackersController < BodyTrackingPluginController
|
|||||||
flash[:notice] += ", #{new_quantities_count > 0 ? new_quantities_count : "no" } new" \
|
flash[:notice] += ", #{new_quantities_count > 0 ? new_quantities_count : "no" } new" \
|
||||||
" #{'quantity'.pluralize(new_quantities_count)}"
|
" #{'quantity'.pluralize(new_quantities_count)}"
|
||||||
|
|
||||||
ncv = @project.nutrients_column_view
|
if @project.nutrient_quantities.empty?
|
||||||
if ncv.quantities.count == 0
|
@project.nutrient_quantities << @project.quantities.diet.roots.first(6)
|
||||||
ncv.quantities.append(@project.quantities.roots.first(6))
|
|
||||||
ncv.save!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sources
|
# Sources
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
class BodyTrackingPluginController < ApplicationController
|
|
||||||
menu_item :body_trackers
|
|
||||||
layout 'body_tracking'
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def find_quantity(id = :id)
|
|
||||||
@quantity = Quantity.find(params[id])
|
|
||||||
@project = @quantity.project
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
render_404
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_quantity_by_quantity_id
|
|
||||||
find_quantity(:quantity_id)
|
|
||||||
end
|
|
||||||
end
|
|
42
app/controllers/concerns/finders.rb
Normal file
42
app/controllers/concerns/finders.rb
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
module Concerns::Finders
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_ingredient
|
||||||
|
@ingredient = Ingredient.find(params[:id])
|
||||||
|
@project = @ingredient.project
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_measurement
|
||||||
|
@measurement = Measurement.find(params[:id])
|
||||||
|
@project = @measurement.routine.project
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_measurement_routine
|
||||||
|
@routine = MeasurementRoutine.find(params[:id])
|
||||||
|
@project = @routine.project
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_quantity(id = :id)
|
||||||
|
@quantity = Quantity.find(params[id])
|
||||||
|
@project = @quantity.project
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_quantity_by_quantity_id
|
||||||
|
find_quantity(:quantity_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_unit
|
||||||
|
@unit = Unit.find(params[:id])
|
||||||
|
@project = @unit.project
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render_404
|
||||||
|
end
|
||||||
|
end
|
@ -1,9 +1,13 @@
|
|||||||
class IngredientsController < BodyTrackingPluginController
|
class IngredientsController < ApplicationController
|
||||||
require 'csv'
|
require 'csv'
|
||||||
|
|
||||||
|
layout 'body_tracking'
|
||||||
|
menu_item :body_trackers
|
||||||
helper :body_trackers
|
helper :body_trackers
|
||||||
helper_method :current_view
|
helper_method :current_view
|
||||||
|
|
||||||
|
include Concerns::Finders
|
||||||
|
|
||||||
before_action :init_session_filters
|
before_action :init_session_filters
|
||||||
before_action :find_project_by_project_id,
|
before_action :find_project_by_project_id,
|
||||||
only: [:index, :new, :create, :nutrients, :filter, :import]
|
only: [:index, :new, :create, :nutrients, :filter, :import]
|
||||||
@ -63,7 +67,7 @@ class IngredientsController < BodyTrackingPluginController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def toggle_column
|
def toggle_column
|
||||||
@project.nutrients_column_view.toggle_column!(@quantity)
|
@project.nutrient_columns.toggle!(@quantity)
|
||||||
prepare_items
|
prepare_items
|
||||||
render :index
|
render :index
|
||||||
end
|
end
|
||||||
@ -201,15 +205,6 @@ class IngredientsController < BodyTrackingPluginController
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# :find_* methods are called before :authorize,
|
|
||||||
# @project is required for :authorize to succeed
|
|
||||||
def find_ingredient
|
|
||||||
@ingredient = Ingredient.find(params[:id])
|
|
||||||
@project = @ingredient.project
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
render_404
|
|
||||||
end
|
|
||||||
|
|
||||||
def prepare_ingredients
|
def prepare_ingredients
|
||||||
@ingredients, @formula_q = @project.ingredients
|
@ingredients, @formula_q = @project.ingredients
|
||||||
.includes(:ref_unit, :source)
|
.includes(:ref_unit, :source)
|
||||||
@ -217,7 +212,7 @@ class IngredientsController < BodyTrackingPluginController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def prepare_nutrients
|
def prepare_nutrients
|
||||||
@quantities = @project.nutrients_column_view.quantities.includes(:formula)
|
@quantities = @project.nutrient_quantities.includes(:formula)
|
||||||
@ingredients, @requested_n, @extra_n, @formula_q = @project.ingredients
|
@ingredients, @requested_n, @extra_n, @formula_q = @project.ingredients
|
||||||
.filter(session[:i_filters], @quantities)
|
.filter(session[:i_filters], @quantities)
|
||||||
end
|
end
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
class MeasurementsController < BodyTrackingPluginController
|
class MeasurementsController < ApplicationController
|
||||||
|
layout 'body_tracking'
|
||||||
|
menu_item :body_trackers
|
||||||
helper :body_trackers
|
helper :body_trackers
|
||||||
|
|
||||||
|
include Concerns::Finders
|
||||||
|
|
||||||
before_action :init_session_filters
|
before_action :init_session_filters
|
||||||
before_action :find_project_by_project_id, only: [:index, :new, :create, :filter]
|
before_action :find_project_by_project_id, only: [:index, :new, :create, :filter]
|
||||||
before_action :find_quantity_by_quantity_id, only: [:toggle_column]
|
before_action :find_quantity_by_quantity_id, only: [:toggle_column]
|
||||||
before_action :find_measurement,
|
before_action :find_measurement, only: [:edit, :update, :destroy, :retake]
|
||||||
only: [:edit, :update, :destroy, :retake, :readouts, :toggle_column]
|
before_action :find_measurement_routine, only: [:readouts, :toggle_column]
|
||||||
before_action :authorize
|
before_action :authorize
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@ -15,12 +19,18 @@ class MeasurementsController < BodyTrackingPluginController
|
|||||||
|
|
||||||
def new
|
def new
|
||||||
@measurement = @project.measurements.new
|
@measurement = @project.measurements.new
|
||||||
|
@measurement.build_routine
|
||||||
@measurement.readouts.new
|
@measurement.readouts.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@measurement = @project.measurements.new(measurement_params)
|
@measurement = @project.measurements.new(measurement_params)
|
||||||
|
@measurement.routine.project = @project
|
||||||
if @measurement.save
|
if @measurement.save
|
||||||
|
if @measurement.routine.columns.empty?
|
||||||
|
@measurement.routine.quantities << @measurement.readouts.map(&:quantity).first(6)
|
||||||
|
end
|
||||||
|
|
||||||
flash[:notice] = 'Created new measurement'
|
flash[:notice] = 'Created new measurement'
|
||||||
readouts_view? ? prepare_readouts : prepare_measurements
|
readouts_view? ? prepare_readouts : prepare_measurements
|
||||||
else
|
else
|
||||||
@ -58,12 +68,12 @@ class MeasurementsController < BodyTrackingPluginController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def readouts
|
def readouts
|
||||||
session[:m_filters][:scope] = {name: @measurement.name}
|
#session[:m_filters][:scope] = {routine: @routine}
|
||||||
prepare_readouts
|
prepare_readouts
|
||||||
end
|
end
|
||||||
|
|
||||||
def toggle_column
|
def toggle_column
|
||||||
@measurement.column_view.toggle_column!(@quantity)
|
@routine.columns.toggle!(@quantity)
|
||||||
prepare_readouts
|
prepare_readouts
|
||||||
render :index
|
render :index
|
||||||
end
|
end
|
||||||
@ -82,8 +92,13 @@ class MeasurementsController < BodyTrackingPluginController
|
|||||||
|
|
||||||
def measurement_params
|
def measurement_params
|
||||||
params.require(:measurement).permit(
|
params.require(:measurement).permit(
|
||||||
:name,
|
:notes,
|
||||||
:source_id,
|
:source_id,
|
||||||
|
routine_attributes:
|
||||||
|
[
|
||||||
|
:name,
|
||||||
|
:description
|
||||||
|
],
|
||||||
readouts_attributes:
|
readouts_attributes:
|
||||||
[
|
[
|
||||||
:id,
|
:id,
|
||||||
@ -95,30 +110,17 @@ class MeasurementsController < BodyTrackingPluginController
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# :find_* methods are called before :authorize,
|
|
||||||
# @project is required for :authorize to succeed
|
|
||||||
def find_measurement
|
|
||||||
@measurement = Measurement.find(params[:id])
|
|
||||||
@project = @measurement.project
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
render_404
|
|
||||||
end
|
|
||||||
|
|
||||||
def prepare_measurements
|
def prepare_measurements
|
||||||
@measurements, @formula_q = @project.measurements
|
@measurements, @formula_q = @project.measurements
|
||||||
.includes(:source, :readouts)
|
.includes(:routine, :source, :readouts)
|
||||||
.filter(session[:m_filters])
|
.filter(session[:m_filters])
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_readouts
|
def prepare_readouts
|
||||||
@scoping_measurement = @project.measurements.where(session[:m_filters][:scope]).first!
|
@quantities = @routine.quantities.includes(:formula)
|
||||||
@quantities = @scoping_measurement.column_view.quantities.includes(:formula)
|
@measurements, @requested_r, @extra_r, @formula_q = @routine.measurements
|
||||||
@measurements, @requested_r, @extra_r, @formula_q = @project.measurements
|
.includes(:routine, :source)
|
||||||
.includes(:source)
|
|
||||||
.filter(session[:m_filters], @quantities)
|
.filter(session[:m_filters], @quantities)
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
session[:m_filters][:scope] = {}
|
|
||||||
render_404
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def readouts_view?
|
def readouts_view?
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
class QuantitiesController < BodyTrackingPluginController
|
class QuantitiesController < ApplicationController
|
||||||
|
layout 'body_tracking'
|
||||||
|
menu_item :body_trackers
|
||||||
helper :body_trackers
|
helper :body_trackers
|
||||||
|
|
||||||
|
include Concerns::Finders
|
||||||
|
|
||||||
before_action :init_session_filters
|
before_action :init_session_filters
|
||||||
before_action :find_project_by_project_id, only: [:index, :new, :create, :filter, :parents]
|
before_action :find_project_by_project_id, only: [:index, :new, :create, :filter, :parents]
|
||||||
before_action :find_quantity, only: [:edit, :update, :destroy, :move,
|
before_action :find_quantity, only: [:edit, :update, :destroy, :move,
|
||||||
@ -118,6 +122,6 @@ class QuantitiesController < BodyTrackingPluginController
|
|||||||
|
|
||||||
def prepare_quantities
|
def prepare_quantities
|
||||||
@quantities = @project.quantities.filter(@project, session[:q_filters])
|
@quantities = @project.quantities.filter(@project, session[:q_filters])
|
||||||
.includes(:column_views, :formula, :parent)
|
.includes(:columns, :formula, :parent)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
class SourcesController < BodyTrackingPluginController
|
class SourcesController < ApplicationController
|
||||||
|
layout 'body_tracking'
|
||||||
|
menu_item :body_trackers
|
||||||
|
|
||||||
before_action :find_project_by_project_id, only: [:index, :create]
|
before_action :find_project_by_project_id, only: [:index, :create]
|
||||||
before_action :find_source, only: [:destroy]
|
before_action :find_source, only: [:destroy]
|
||||||
before_action :authorize
|
before_action :authorize
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
class UnitsController < BodyTrackingPluginController
|
class UnitsController < ApplicationController
|
||||||
|
layout 'body_tracking'
|
||||||
|
menu_item :body_trackers
|
||||||
|
|
||||||
|
include Concerns::Finders
|
||||||
|
|
||||||
before_action :find_project_by_project_id, only: [:index, :create]
|
before_action :find_project_by_project_id, only: [:index, :create]
|
||||||
before_action :find_unit, only: [:destroy]
|
before_action :find_unit, only: [:destroy]
|
||||||
before_action :authorize
|
before_action :authorize
|
||||||
@ -34,13 +39,4 @@ class UnitsController < BodyTrackingPluginController
|
|||||||
:shortname
|
:shortname
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# :find_* methods are called before :authorize,
|
|
||||||
# @project is required for :authorize to succeed
|
|
||||||
def find_unit
|
|
||||||
@unit = Unit.find(params[:id])
|
|
||||||
@project = @unit.project
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
render_404
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -7,9 +7,9 @@ module IngredientsHelper
|
|||||||
|
|
||||||
def toggle_column_options
|
def toggle_column_options
|
||||||
disabled = []
|
disabled = []
|
||||||
enabled_columns = @project.nutrients_column_view.quantities.to_a
|
enabled_quantities = @project.nutrient_quantities.to_a
|
||||||
options = nested_set_options(@project.quantities.diet) do |q|
|
options = nested_set_options(@project.quantities.diet) do |q|
|
||||||
disabled << q.id if enabled_columns.include?(q)
|
disabled << q.id if enabled_quantities.include?(q)
|
||||||
raw("#{' ' * q.level}#{q.name}")
|
raw("#{' ' * q.level}#{q.name}")
|
||||||
end
|
end
|
||||||
options_for_select(options, disabled: disabled)
|
options_for_select(options, disabled: disabled)
|
||||||
|
@ -11,9 +11,9 @@ module MeasurementsHelper
|
|||||||
|
|
||||||
def toggle_column_options
|
def toggle_column_options
|
||||||
disabled = []
|
disabled = []
|
||||||
enabled_columns = @scoping_measurement.column_view.quantities
|
enabled_quantities = @routine.quantities.to_a
|
||||||
options = nested_set_options(@project.quantities.measurement) do |q|
|
options = nested_set_options(@project.quantities.measurement) do |q|
|
||||||
disabled << q.id if enabled_columns.include?(q)
|
disabled << q.id if enabled_quantities.include?(q)
|
||||||
raw("#{' ' * q.level}#{q.name}")
|
raw("#{' ' * q.level}#{q.name}")
|
||||||
end
|
end
|
||||||
options_for_select(options, disabled: disabled)
|
options_for_select(options, disabled: disabled)
|
||||||
|
4
app/models/column.rb
Normal file
4
app/models/column.rb
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
class Column < ActiveRecord::Base
|
||||||
|
belongs_to :column_view, polymorphic: true
|
||||||
|
belongs_to :quantity
|
||||||
|
end
|
@ -1,19 +0,0 @@
|
|||||||
class ColumnView < ActiveRecord::Base
|
|
||||||
enum domain: Quantity.domains
|
|
||||||
|
|
||||||
belongs_to :project, required: true
|
|
||||||
has_and_belongs_to_many :quantities, -> { order "lft" }
|
|
||||||
|
|
||||||
validates :name, presence: true, uniqueness: {scope: :domain}
|
|
||||||
validates :domain, inclusion: {in: domains.keys}
|
|
||||||
|
|
||||||
# TODO: enforce column_view - quantity 'domain' identity
|
|
||||||
def toggle_column!(q)
|
|
||||||
column = self.quantities.find(q.id)
|
|
||||||
self.quantities.destroy(column)
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
# Cannot 'create' association, as ColumnView (parent) may not be saved yet
|
|
||||||
self.quantities.append(q)
|
|
||||||
self.save!
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,5 +1,11 @@
|
|||||||
class Measurement < ActiveRecord::Base
|
class Measurement < ActiveRecord::Base
|
||||||
belongs_to :project, required: true
|
belongs_to :routine, required: true, inverse_of: :measurements,
|
||||||
|
class_name: 'MeasurementRoutine'
|
||||||
|
accepts_nested_attributes_for :routine, allow_destroy: true, reject_if: proc { |attrs|
|
||||||
|
attrs['name'].blank?
|
||||||
|
}
|
||||||
|
after_destroy { self.routine.destroy if self.routine.measurements.empty? }
|
||||||
|
|
||||||
belongs_to :source, required: false
|
belongs_to :source, required: false
|
||||||
|
|
||||||
has_many :readouts, inverse_of: :measurement, dependent: :destroy, validate: true
|
has_many :readouts, inverse_of: :measurement, dependent: :destroy, validate: true
|
||||||
@ -17,7 +23,6 @@ class Measurement < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
validates :name, presence: true
|
|
||||||
validates :taken_at, presence: true
|
validates :taken_at, presence: true
|
||||||
|
|
||||||
after_initialize do
|
after_initialize do
|
||||||
@ -26,21 +31,6 @@ class Measurement < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
after_create :seed_column_view, if: -> {self.column_view.quantities.count == 0}
|
|
||||||
after_save :cleanup_column_view, if: :name_changed?
|
|
||||||
|
|
||||||
# Destroy ColumnView after last Measurement destruction
|
|
||||||
after_destroy do
|
|
||||||
unless self.project.measurements.exists?(name: self.name)
|
|
||||||
self.column_view.destroy!
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def column_view
|
|
||||||
self.project.column_views
|
|
||||||
.find_or_create_by(name: self.name, domain: ColumnView.domains[:measurement])
|
|
||||||
end
|
|
||||||
|
|
||||||
def taken_at_date
|
def taken_at_date
|
||||||
self.taken_at.getlocal
|
self.taken_at.getlocal
|
||||||
end
|
end
|
||||||
@ -54,27 +44,4 @@ class Measurement < ActiveRecord::Base
|
|||||||
def taken_at_time=(value)
|
def taken_at_time=(value)
|
||||||
self.taken_at = Time.parse(value, self.taken_at)
|
self.taken_at = Time.parse(value, self.taken_at)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def seed_column_view
|
|
||||||
quantities = self.project.quantities.joins(:readouts).includes(readouts: [:measurement])
|
|
||||||
.where(measurements: {name: self.name}).first(6)
|
|
||||||
self.column_view.quantities.append(quantities)
|
|
||||||
self.column_view.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Copy/rename ColumnView on Measurement rename
|
|
||||||
def cleanup_column_view
|
|
||||||
old_column_view = self.project.column_views
|
|
||||||
.find_by(name: self.name_was, domain: ColumnView.domains[:measurement])
|
|
||||||
return unless old_column_view
|
|
||||||
|
|
||||||
if self.project.measurements.exists?(name: self.name_was)
|
|
||||||
self.column_view.quantities.append(old_column_view.quantities)
|
|
||||||
self.column_view.save!
|
|
||||||
else
|
|
||||||
old_column_view.update!(name: self.name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
11
app/models/measurement_routine.rb
Normal file
11
app/models/measurement_routine.rb
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
class MeasurementRoutine < ActiveRecord::Base
|
||||||
|
belongs_to :project, required: true
|
||||||
|
has_many :measurements, -> { order "taken_at DESC" }, inverse_of: :routine,
|
||||||
|
foreign_key: 'routine_id', dependent: :restrict_with_error,
|
||||||
|
extend: BodyTracking::ItemsWithQuantities
|
||||||
|
has_many :columns, as: :column_view, dependent: :destroy,
|
||||||
|
extend: BodyTracking::TogglableColumns
|
||||||
|
has_many :quantities, -> { order "lft" }, through: :columns
|
||||||
|
|
||||||
|
validates :name, presence: true, uniqueness: {scope: :project_id}
|
||||||
|
end
|
@ -5,16 +5,11 @@ class Quantity < ActiveRecord::Base
|
|||||||
exercise: 2
|
exercise: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
# Has to go before any 'dependent:' association
|
|
||||||
before_destroy do
|
|
||||||
# FIXME: disallow destruction if any object depends on this quantity
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
acts_as_nested_set dependent: :destroy, scope: :project
|
acts_as_nested_set dependent: :destroy, scope: :project
|
||||||
belongs_to :project, required: false
|
belongs_to :project, required: false
|
||||||
has_and_belongs_to_many :column_views
|
has_many :nutrients, dependent: :restrict_with_error
|
||||||
has_many :readouts
|
has_many :readouts, dependent: :restrict_with_error
|
||||||
|
has_many :columns, dependent: :destroy
|
||||||
|
|
||||||
has_one :formula, inverse_of: :quantity, dependent: :destroy, validate: true
|
has_one :formula, inverse_of: :quantity, dependent: :destroy, validate: true
|
||||||
accepts_nested_attributes_for :formula, allow_destroy: true, reject_if: proc { |attrs|
|
accepts_nested_attributes_for :formula, allow_destroy: true, reject_if: proc { |attrs|
|
||||||
|
@ -1,17 +1,29 @@
|
|||||||
<%= error_messages_for @measurement %>
|
<%= error_messages_for @measurement %>
|
||||||
|
|
||||||
<div class="box tabular">
|
<fieldset class="box">
|
||||||
<p><%= f.select :source_id, source_options,
|
<legend ><%= t('.label_routine') %></legend>
|
||||||
{required: false, include_blank: t('.null_source')} %></p>
|
<div class="tabular">
|
||||||
<p><%= f.text_field :name, size: 40, required: true %></p>
|
<%= f.fields_for :routine do |ff| %>
|
||||||
<p>
|
<p><%= ff.text_field :name, required: true, style: "width: 95%;" %></p>
|
||||||
|
<p><%= ff.text_area :description, cols: 40, rows: 3, style: "width: 95%;" %></p>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset class="box">
|
||||||
|
<legend ><%= t('.label_measurement') %></legend>
|
||||||
|
<div class="tabular">
|
||||||
|
<p><%= f.select :source_id, source_options,
|
||||||
|
{required: false, include_blank: t('.null_source')} %></p>
|
||||||
|
<p><%= f.text_area :notes, cols: 40, rows: 1, style: "width: 95%;" %></p>
|
||||||
|
<p>
|
||||||
<%= f.date_field :taken_at_date, required: true %>
|
<%= f.date_field :taken_at_date, required: true %>
|
||||||
<%= f.time_field :taken_at_time, value: format_time(@measurement), required: true,
|
<%= f.time_field :taken_at_time, value: format_time(@measurement), required: true,
|
||||||
label: '' %>
|
label: '' %>
|
||||||
</p>
|
</p>
|
||||||
<% @measurement.readouts.each_with_index do |r, index| %>
|
<% @measurement.readouts.each_with_index do |r, index| %>
|
||||||
<%= f.fields_for 'readouts_attributes', r, index: '' do |ff| %>
|
<%= f.fields_for 'readouts_attributes', r, index: '' do |ff| %>
|
||||||
<p class="readout">
|
<p class="readout">
|
||||||
<%= ff.hidden_field :id %>
|
<%= ff.hidden_field :id %>
|
||||||
<%= ff.select :quantity_id, quantity_options,
|
<%= ff.select :quantity_id, quantity_options,
|
||||||
{include_blank: true, required: true, label: (index > 0 ? '' : :field_readouts)} %>
|
{include_blank: true, required: true, label: (index > 0 ? '' : :field_readouts)} %>
|
||||||
@ -22,14 +34,15 @@
|
|||||||
class: 'icon icon-del',
|
class: 'icon icon-del',
|
||||||
style: (@measurement.readouts.length > 1 ? "" : "display:none"),
|
style: (@measurement.readouts.length > 1 ? "" : "display:none"),
|
||||||
onclick: "deleteReadout(); return false;" %>
|
onclick: "deleteReadout(); return false;" %>
|
||||||
</p>
|
</p>
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<p>
|
||||||
<p>
|
|
||||||
<%= link_to t(".button_new_readout"), '#', class: 'icon icon-add',
|
<%= link_to t(".button_new_readout"), '#', class: 'icon icon-add',
|
||||||
onclick: 'newReadout(); return false;' %>
|
onclick: 'newReadout(); return false;' %>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<%= javascript_tag do %>
|
<%= javascript_tag do %>
|
||||||
function newReadout() {
|
function newReadout() {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<td class="date unwrappable"><%= format_datetime(m) %></td>
|
<td class="date unwrappable"><%= format_datetime(m) %></td>
|
||||||
<td class="name">
|
<td class="name">
|
||||||
<div style="float:left;">
|
<div style="float:left;">
|
||||||
<%= link_to m.name, readouts_measurement_path(m) %>
|
<%= link_to m.routine.name, readouts_measurement_routine_path(m.routine) %>
|
||||||
</div>
|
</div>
|
||||||
<div style="float:right;">
|
<div style="float:right;">
|
||||||
<small><%= " (#{pluralize(m.readouts.size, 'readout')})" %></small>
|
<small><%= " (#{pluralize(m.readouts.size, 'readout')})" %></small>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<fieldset id="options" class="collapsible">
|
<fieldset id="options" class="collapsible">
|
||||||
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
|
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
|
||||||
<div>
|
<div>
|
||||||
<%= form_tag toggle_column_measurement_path(@scoping_measurement),
|
<%= form_tag toggle_column_measurement_routine_path(@routine),
|
||||||
id: 'toggle-column-form', name: 'toggle-column-form',
|
id: 'toggle-column-form', name: 'toggle-column-form',
|
||||||
method: :post, remote: true do %>
|
method: :post, remote: true do %>
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
<th style="width:<%= 100/total_width %>%" class="closable ellipsible">
|
<th style="width:<%= 100/total_width %>%" class="closable ellipsible">
|
||||||
<div style="float:right;position:relative;">
|
<div style="float:right;position:relative;">
|
||||||
<%= link_to '',
|
<%= link_to '',
|
||||||
toggle_column_measurement_path(@scoping_measurement, quantity_id: q.id),
|
toggle_column_measurement_routine_path(@routine, quantity_id: q.id),
|
||||||
{class: "icon icon-close", method: :post, remote: true} %>
|
{class: "icon icon-close", method: :post, remote: true} %>
|
||||||
</div>
|
</div>
|
||||||
<%= q.name %>
|
<%= q.name %>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<h2>
|
<h2>
|
||||||
<%= link_to t("measurements.index.heading"), project_measurements_path(@project) %>
|
<%= link_to t("measurements.index.heading"), project_measurements_path(@project) %>
|
||||||
>
|
>
|
||||||
<%= @measurements.first.name %>
|
<%= @routine.name %>
|
||||||
</h2>
|
</h2>
|
||||||
<div id='readouts'>
|
<div id='readouts'>
|
||||||
<%= render partial: 'measurements/readouts' %>
|
<%= render partial: 'measurements/readouts' %>
|
||||||
|
@ -18,12 +18,12 @@
|
|||||||
<%
|
<%
|
||||||
next if q.new_record?
|
next if q.new_record?
|
||||||
quantity_class = "quantity"
|
quantity_class = "quantity"
|
||||||
quantity_class += " primary" unless q.column_views.empty?
|
quantity_class += " primary" unless q.columns.empty?
|
||||||
quantity_class += " project idnt idnt-#{level+1}"
|
quantity_class += " project idnt idnt-#{level+1}"
|
||||||
%>
|
%>
|
||||||
<tr id="quantity-<%= q.id %>" class="<%= quantity_class %>">
|
<tr id="quantity-<%= q.id %>" class="<%= quantity_class %>">
|
||||||
<td class="name unwrappable">
|
<td class="name unwrappable">
|
||||||
<div class="icon <%= q.column_views.empty? ? 'icon-fav-off' : 'icon-fav' %>">
|
<div class="icon <%= q.columns.empty? ? 'icon-fav-off' : 'icon-fav' %>">
|
||||||
<%= q.name %>
|
<%= q.name %>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
@ -37,7 +37,7 @@ input[type=number] {
|
|||||||
-moz-appearance:textfield;
|
-moz-appearance:textfield;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
input[type=date], input[type=time], input[type=number] {
|
input[type=date], input[type=time], input[type=number], textarea {
|
||||||
border:1px solid #9EB1C2;
|
border:1px solid #d7d7d7;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,8 @@ en:
|
|||||||
filters:
|
filters:
|
||||||
zero_nil: 'missing -> 0?'
|
zero_nil: 'missing -> 0?'
|
||||||
form:
|
form:
|
||||||
|
label_routine: 'Measurement routine (shared among all measurements of this kind)'
|
||||||
|
label_measurement: 'Measurement'
|
||||||
button_new_readout: 'Add readout'
|
button_new_readout: 'Add readout'
|
||||||
button_delete_readout: 'Delete'
|
button_delete_readout: 'Delete'
|
||||||
null_source: '- unspecified -'
|
null_source: '- unspecified -'
|
||||||
|
@ -5,38 +5,42 @@ resources :projects, shallow: true do
|
|||||||
resources :body_trackers, only: [:index] do
|
resources :body_trackers, only: [:index] do
|
||||||
collection do
|
collection do
|
||||||
post 'defaults'
|
post 'defaults'
|
||||||
resources :measurements, only: [:index, :new, :create, :edit, :update, :destroy] do
|
|
||||||
member do
|
|
||||||
get 'retake'
|
|
||||||
get 'readouts'
|
|
||||||
post 'toggle_column'
|
|
||||||
end
|
|
||||||
collection do
|
|
||||||
get 'filter'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
resources :ingredients, only: [:index, :new, :create, :edit, :update, :destroy] do
|
|
||||||
post 'toggle', on: :member
|
|
||||||
collection do
|
|
||||||
get 'nutrients'
|
|
||||||
post 'toggle_column'
|
|
||||||
get 'filter'
|
|
||||||
post 'import'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
resources :sources, only: [:index, :create, :destroy]
|
|
||||||
resources :quantities, only: [:index, :new, :create, :edit, :update, :destroy] do
|
|
||||||
member do
|
|
||||||
get 'new_child'
|
|
||||||
post 'create_child'
|
|
||||||
post 'move/:direction', to: 'quantities#move', as: :move
|
|
||||||
end
|
|
||||||
collection do
|
|
||||||
get 'parents'
|
|
||||||
get 'filter'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
resources :units, only: [:index, :create, :destroy]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
resources :measurement_routines, only: [] do
|
||||||
|
member do
|
||||||
|
get 'readouts', to: 'measurements#readouts'
|
||||||
|
post 'toggle_column', to: 'measurements#toggle_column'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :measurements, only: [:index, :new, :create, :edit, :update, :destroy] do
|
||||||
|
member do
|
||||||
|
get 'retake'
|
||||||
|
end
|
||||||
|
collection do
|
||||||
|
get 'filter'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :ingredients, only: [:index, :new, :create, :edit, :update, :destroy] do
|
||||||
|
post 'toggle', on: :member
|
||||||
|
collection do
|
||||||
|
get 'nutrients'
|
||||||
|
post 'toggle_column'
|
||||||
|
get 'filter'
|
||||||
|
post 'import'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :sources, only: [:index, :create, :destroy]
|
||||||
|
resources :quantities, only: [:index, :new, :create, :edit, :update, :destroy] do
|
||||||
|
member do
|
||||||
|
get 'new_child'
|
||||||
|
post 'create_child'
|
||||||
|
post 'move/:direction', to: 'quantities#move', as: :move
|
||||||
|
end
|
||||||
|
collection do
|
||||||
|
get 'parents'
|
||||||
|
get 'filter'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :units, only: [:index, :create, :destroy]
|
||||||
end
|
end
|
||||||
|
@ -27,14 +27,8 @@ class CreateSchema < ActiveRecord::Migration
|
|||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table :column_views do |t|
|
create_table :columns do |t|
|
||||||
t.references :project
|
t.references :column_view, polymorphic: true
|
||||||
t.string :name
|
|
||||||
t.integer :domain
|
|
||||||
end
|
|
||||||
|
|
||||||
create_table :column_views_quantities do |t|
|
|
||||||
t.references :column_view
|
|
||||||
t.references :quantity
|
t.references :quantity
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -66,10 +60,17 @@ class CreateSchema < ActiveRecord::Migration
|
|||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table :measurements do |t|
|
create_table :measurement_routines do |t|
|
||||||
t.references :project
|
t.references :project
|
||||||
t.string :name
|
t.string :name
|
||||||
|
t.text :description
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :measurements do |t|
|
||||||
|
t.references :routine
|
||||||
t.references :source
|
t.references :source
|
||||||
|
t.text :notes
|
||||||
t.timestamp :taken_at
|
t.timestamp :taken_at
|
||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
module BodyTracking
|
module BodyTracking
|
||||||
module ItemsWithQuantities
|
module ItemsWithQuantities
|
||||||
QUANTITY_DOMAINS = {
|
RELATIONS = {
|
||||||
Measurement => :measurement,
|
'Ingredient' => {
|
||||||
Ingredient => :diet
|
domain: :diet,
|
||||||
}
|
foreign_key: :ingredient_id,
|
||||||
VALUE_COLUMNS = {
|
subitem_class: Nutrient,
|
||||||
Measurement => :value,
|
value_field: :amount
|
||||||
Ingredient => :amount
|
},
|
||||||
|
'Measurement' => {
|
||||||
|
domain: :measurement,
|
||||||
|
foreign_key: :measurement_id,
|
||||||
|
subitem_class: Readout,
|
||||||
|
value_field: :value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def filter(filters, requested_q = nil)
|
def filter(filters, requested_q = nil)
|
||||||
@ -20,13 +26,18 @@ module BodyTracking
|
|||||||
items = items.where(hidden: filters[:visibility] == "1" ? false : true)
|
items = items.where(hidden: filters[:visibility] == "1" ? false : true)
|
||||||
end
|
end
|
||||||
|
|
||||||
project = proxy_association.owner
|
formula_q =
|
||||||
domain = QUANTITY_DOMAINS[proxy_association.klass]
|
if filters[:formula].present?
|
||||||
formula_q = if filters[:formula].present?
|
owner = proxy_association.owner
|
||||||
project.quantities.new(name: 'Filter formula',
|
project = owner.is_a?(Project) ? owner : owner.project
|
||||||
formula_attributes: filters[:formula],
|
domain = RELATIONS[proxy_association.klass.name][:domain]
|
||||||
domain: domain)
|
formula_q_attrs = {
|
||||||
end
|
name: 'Filter formula',
|
||||||
|
formula_attributes: filters[:formula],
|
||||||
|
domain: domain
|
||||||
|
}
|
||||||
|
project.quantities.new(formula_q_attrs)
|
||||||
|
end
|
||||||
apply_formula = formula_q.present? && formula_q.valid?
|
apply_formula = formula_q.present? && formula_q.valid?
|
||||||
|
|
||||||
result =
|
result =
|
||||||
@ -45,15 +56,13 @@ module BodyTracking
|
|||||||
unchecked_q = requested_q.map { |q| [q, nil] }
|
unchecked_q = requested_q.map { |q| [q, nil] }
|
||||||
unchecked_q << [filter_q, nil] if filter_q
|
unchecked_q << [filter_q, nil] if filter_q
|
||||||
|
|
||||||
item_class = proxy_association.klass
|
relations = RELATIONS[proxy_association.klass.name]
|
||||||
subitem_type = item_class.nested_attributes_options.keys.first.to_s
|
|
||||||
subitem_reflection = item_class.reflections[subitem_type]
|
|
||||||
subitem_class = subitem_reflection.klass
|
|
||||||
subitems_scope = subitem_class.where(subitem_reflection.options[:inverse_of] => items)
|
|
||||||
subitems = Hash.new { |h,k| h[k] = {} }
|
subitems = Hash.new { |h,k| h[k] = {} }
|
||||||
subitems_scope.includes(:quantity, :unit).order('quantities.lft').each do |s|
|
relations[:subitem_class].where(relations[:foreign_key] => items)
|
||||||
item_id = s.send(subitem_reflection.foreign_key)
|
.includes(:quantity, :unit).order('quantities.lft').each do |s|
|
||||||
subitem_value = s.send(VALUE_COLUMNS[item_class])
|
|
||||||
|
item_id = s.send(relations[:foreign_key])
|
||||||
|
subitem_value = s.send(relations[:value_field])
|
||||||
subitems[s.quantity][item_id] = [subitem_value, s.unit]
|
subitems[s.quantity][item_id] = [subitem_value, s.unit]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
module BodyTracking
|
module BodyTracking::ProjectPatch
|
||||||
module ProjectPatch
|
Project.class_eval do
|
||||||
Project.class_eval do
|
has_many :measurement_routines, dependent: :destroy
|
||||||
has_many :measurements, -> { order "taken_at DESC" }, dependent: :destroy,
|
has_many :measurements, -> { order "taken_at DESC" }, dependent: :destroy,
|
||||||
extend: ItemsWithQuantities
|
extend: BodyTracking::ItemsWithQuantities, through: :measurement_routines
|
||||||
has_many :ingredients, -> { order "name" }, dependent: :destroy,
|
has_many :ingredients, -> { order "name" }, dependent: :destroy,
|
||||||
extend: ItemsWithQuantities
|
extend: BodyTracking::ItemsWithQuantities
|
||||||
|
|
||||||
has_many :sources, dependent: :destroy
|
has_many :sources, dependent: :destroy
|
||||||
has_many :column_views, dependent: :destroy
|
has_many :quantities, -> { order "lft" }, dependent: :destroy
|
||||||
has_many :quantities, -> { order "lft" }, dependent: :destroy
|
has_many :units, dependent: :destroy
|
||||||
has_many :units, dependent: :destroy
|
|
||||||
|
|
||||||
def nutrients_column_view
|
has_many :nutrient_columns, as: :column_view, dependent: :destroy, class_name: 'Column',
|
||||||
self.column_views
|
extend: BodyTracking::TogglableColumns
|
||||||
.find_or_create_by(name: 'Nutrients', domain: ColumnView.domains[:diet])
|
has_many :nutrient_quantities, -> { order "lft" }, through: :nutrient_columns,
|
||||||
end
|
source: 'quantity'
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
7
lib/body_tracking/togglable_columns.rb
Normal file
7
lib/body_tracking/togglable_columns.rb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module BodyTracking::TogglableColumns
|
||||||
|
# TODO: enforce 'domain' identity between quantites and receiving collection?
|
||||||
|
def toggle!(q)
|
||||||
|
column = find_by(quantity: q)
|
||||||
|
column ? destroy(column) : create(quantity: q)
|
||||||
|
end
|
||||||
|
end
|
Reference in New Issue
Block a user