1
0

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:
cryptogopher
2020-03-29 00:56:37 +01:00
parent e7a33c684f
commit 18419f1aeb
28 changed files with 260 additions and 243 deletions

View File

@@ -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 :authorize
@@ -43,10 +46,8 @@ class BodyTrackersController < BodyTrackingPluginController
flash[:notice] += ", #{new_quantities_count > 0 ? new_quantities_count : "no" } new" \
" #{'quantity'.pluralize(new_quantities_count)}"
ncv = @project.nutrients_column_view
if ncv.quantities.count == 0
ncv.quantities.append(@project.quantities.roots.first(6))
ncv.save!
if @project.nutrient_quantities.empty?
@project.nutrient_quantities << @project.quantities.diet.roots.first(6)
end
# Sources

View File

@@ -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

View 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

View File

@@ -1,9 +1,13 @@
class IngredientsController < BodyTrackingPluginController
class IngredientsController < ApplicationController
require 'csv'
layout 'body_tracking'
menu_item :body_trackers
helper :body_trackers
helper_method :current_view
include Concerns::Finders
before_action :init_session_filters
before_action :find_project_by_project_id,
only: [:index, :new, :create, :nutrients, :filter, :import]
@@ -63,7 +67,7 @@ class IngredientsController < BodyTrackingPluginController
end
def toggle_column
@project.nutrients_column_view.toggle_column!(@quantity)
@project.nutrient_columns.toggle!(@quantity)
prepare_items
render :index
end
@@ -201,15 +205,6 @@ class IngredientsController < BodyTrackingPluginController
)
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
@ingredients, @formula_q = @project.ingredients
.includes(:ref_unit, :source)
@@ -217,7 +212,7 @@ class IngredientsController < BodyTrackingPluginController
end
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
.filter(session[:i_filters], @quantities)
end

View File

@@ -1,11 +1,15 @@
class MeasurementsController < BodyTrackingPluginController
class MeasurementsController < ApplicationController
layout 'body_tracking'
menu_item :body_trackers
helper :body_trackers
include Concerns::Finders
before_action :init_session_filters
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_measurement,
only: [:edit, :update, :destroy, :retake, :readouts, :toggle_column]
before_action :find_measurement, only: [:edit, :update, :destroy, :retake]
before_action :find_measurement_routine, only: [:readouts, :toggle_column]
before_action :authorize
def index
@@ -15,12 +19,18 @@ class MeasurementsController < BodyTrackingPluginController
def new
@measurement = @project.measurements.new
@measurement.build_routine
@measurement.readouts.new
end
def create
@measurement = @project.measurements.new(measurement_params)
@measurement.routine.project = @project
if @measurement.save
if @measurement.routine.columns.empty?
@measurement.routine.quantities << @measurement.readouts.map(&:quantity).first(6)
end
flash[:notice] = 'Created new measurement'
readouts_view? ? prepare_readouts : prepare_measurements
else
@@ -58,12 +68,12 @@ class MeasurementsController < BodyTrackingPluginController
end
def readouts
session[:m_filters][:scope] = {name: @measurement.name}
#session[:m_filters][:scope] = {routine: @routine}
prepare_readouts
end
def toggle_column
@measurement.column_view.toggle_column!(@quantity)
@routine.columns.toggle!(@quantity)
prepare_readouts
render :index
end
@@ -82,8 +92,13 @@ class MeasurementsController < BodyTrackingPluginController
def measurement_params
params.require(:measurement).permit(
:name,
:notes,
:source_id,
routine_attributes:
[
:name,
:description
],
readouts_attributes:
[
:id,
@@ -95,30 +110,17 @@ class MeasurementsController < BodyTrackingPluginController
)
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
@measurements, @formula_q = @project.measurements
.includes(:source, :readouts)
.includes(:routine, :source, :readouts)
.filter(session[:m_filters])
end
def prepare_readouts
@scoping_measurement = @project.measurements.where(session[:m_filters][:scope]).first!
@quantities = @scoping_measurement.column_view.quantities.includes(:formula)
@measurements, @requested_r, @extra_r, @formula_q = @project.measurements
.includes(:source)
@quantities = @routine.quantities.includes(:formula)
@measurements, @requested_r, @extra_r, @formula_q = @routine.measurements
.includes(:routine, :source)
.filter(session[:m_filters], @quantities)
rescue ActiveRecord::RecordNotFound
session[:m_filters][:scope] = {}
render_404
end
def readouts_view?

View File

@@ -1,6 +1,10 @@
class QuantitiesController < BodyTrackingPluginController
class QuantitiesController < ApplicationController
layout 'body_tracking'
menu_item :body_trackers
helper :body_trackers
include Concerns::Finders
before_action :init_session_filters
before_action :find_project_by_project_id, only: [:index, :new, :create, :filter, :parents]
before_action :find_quantity, only: [:edit, :update, :destroy, :move,
@@ -118,6 +122,6 @@ class QuantitiesController < BodyTrackingPluginController
def prepare_quantities
@quantities = @project.quantities.filter(@project, session[:q_filters])
.includes(:column_views, :formula, :parent)
.includes(:columns, :formula, :parent)
end
end

View File

@@ -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_source, only: [:destroy]
before_action :authorize

View File

@@ -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_unit, only: [:destroy]
before_action :authorize
@@ -34,13 +39,4 @@ class UnitsController < BodyTrackingPluginController
:shortname
)
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

View File

@@ -7,9 +7,9 @@ module IngredientsHelper
def toggle_column_options
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|
disabled << q.id if enabled_columns.include?(q)
disabled << q.id if enabled_quantities.include?(q)
raw("#{'&ensp;' * q.level}#{q.name}")
end
options_for_select(options, disabled: disabled)

View File

@@ -11,9 +11,9 @@ module MeasurementsHelper
def toggle_column_options
disabled = []
enabled_columns = @scoping_measurement.column_view.quantities
enabled_quantities = @routine.quantities.to_a
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("#{'&ensp;' * q.level}#{q.name}")
end
options_for_select(options, disabled: disabled)

4
app/models/column.rb Normal file
View File

@@ -0,0 +1,4 @@
class Column < ActiveRecord::Base
belongs_to :column_view, polymorphic: true
belongs_to :quantity
end

View File

@@ -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

View File

@@ -1,5 +1,11 @@
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
has_many :readouts, inverse_of: :measurement, dependent: :destroy, validate: true
@@ -17,7 +23,6 @@ class Measurement < ActiveRecord::Base
end
end
validates :name, presence: true
validates :taken_at, presence: true
after_initialize do
@@ -26,21 +31,6 @@ class Measurement < ActiveRecord::Base
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
self.taken_at.getlocal
end
@@ -54,27 +44,4 @@ class Measurement < ActiveRecord::Base
def taken_at_time=(value)
self.taken_at = Time.parse(value, self.taken_at)
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

View 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

View File

@@ -5,16 +5,11 @@ class Quantity < ActiveRecord::Base
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
belongs_to :project, required: false
has_and_belongs_to_many :column_views
has_many :readouts
has_many :nutrients, dependent: :restrict_with_error
has_many :readouts, dependent: :restrict_with_error
has_many :columns, dependent: :destroy
has_one :formula, inverse_of: :quantity, dependent: :destroy, validate: true
accepts_nested_attributes_for :formula, allow_destroy: true, reject_if: proc { |attrs|

View File

@@ -1,17 +1,29 @@
<%= error_messages_for @measurement %>
<div class="box tabular">
<p><%= f.select :source_id, source_options,
{required: false, include_blank: t('.null_source')} %></p>
<p><%= f.text_field :name, size: 40, required: true %></p>
<p>
<fieldset class="box">
<legend ><%= t('.label_routine') %></legend>
<div class="tabular">
<%= f.fields_for :routine do |ff| %>
<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.time_field :taken_at_time, value: format_time(@measurement), required: true,
label: '' %>
</p>
<% @measurement.readouts.each_with_index do |r, index| %>
<%= f.fields_for 'readouts_attributes', r, index: '' do |ff| %>
<p class="readout">
</p>
<% @measurement.readouts.each_with_index do |r, index| %>
<%= f.fields_for 'readouts_attributes', r, index: '' do |ff| %>
<p class="readout">
<%= ff.hidden_field :id %>
<%= ff.select :quantity_id, quantity_options,
{include_blank: true, required: true, label: (index > 0 ? '' : :field_readouts)} %>
@@ -22,14 +34,15 @@
class: 'icon icon-del',
style: (@measurement.readouts.length > 1 ? "" : "display:none"),
onclick: "deleteReadout(); return false;" %>
</p>
</p>
<% end %>
<% end %>
<% end %>
<p>
<p>
<%= link_to t(".button_new_readout"), '#', class: 'icon icon-add',
onclick: 'newReadout(); return false;' %>
</p>
</div>
</p>
</div>
</fieldset>
<%= javascript_tag do %>
function newReadout() {

View File

@@ -20,7 +20,7 @@
<td class="date unwrappable"><%= format_datetime(m) %></td>
<td class="name">
<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 style="float:right;">
<small><%= " (#{pluralize(m.readouts.size, 'readout')})" %></small>

View File

@@ -1,7 +1,7 @@
<fieldset id="options" class="collapsible">
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
<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',
method: :post, remote: true do %>

View File

@@ -17,7 +17,7 @@
<th style="width:<%= 100/total_width %>%" class="closable ellipsible">
<div style="float:right;position:relative;">
<%= 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} %>
</div>
<%= q.name %>

View File

@@ -8,7 +8,7 @@
<h2>
<%= link_to t("measurements.index.heading"), project_measurements_path(@project) %>
>
<%= @measurements.first.name %>
<%= @routine.name %>
</h2>
<div id='readouts'>
<%= render partial: 'measurements/readouts' %>

View File

@@ -18,12 +18,12 @@
<%
next if q.new_record?
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}"
%>
<tr id="quantity-<%= q.id %>" class="<%= quantity_class %>">
<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 %>
</div>
</td>