1
0

compute_quantities now returns single hash

Fixed MeasurementsController view management
This commit is contained in:
cryptogopher 2020-04-26 20:21:17 +02:00
parent 9f5f31ca42
commit 4230d72206
27 changed files with 118 additions and 108 deletions

View File

@ -24,15 +24,9 @@ module Concerns::Finders
def find_measurement
@measurement = Measurement.find(params[:id])
@routine = @measurement.routine
@project = @routine.project
rescue ActiveRecord::RecordNotFound
render_404
end
def find_measurement_routine
@routine = MeasurementRoutine.find(params[:id])
@project = @routine.project
# DON'T set @routine here: @routine is a context for :readouts view (set
# elsewhere), not a # @measurement.routine
@project = @measurement.routine.project
rescue ActiveRecord::RecordNotFound
render_404
end
@ -44,6 +38,10 @@ module Concerns::Finders
render_404
end
def find_measurement_routine_by_measurement_routine_id
find_measurement_routine(params[:measurement_routine_id])
end
def find_quantity(id = params[:id])
@quantity = Quantity.find(id)
@project = @quantity.project

View File

@ -209,14 +209,12 @@ class FoodsController < ApplicationController
end
def prepare_foods
@foods, @filter_q = @project.foods
.includes(:ref_unit, :source)
@foods, @filter_q = @project.foods.includes(:ref_unit, :source)
.filter(session[:f_filters])
end
def prepare_nutrients
@quantities = @project.nutrient_quantities.includes(:formula)
@foods, @requested_n, @extra_n, @filter_q = @project.foods
.filter(session[:f_filters], @quantities)
@foods, @filter_q = @project.foods.filter(session[:f_filters], @quantities)
end
end

View File

@ -82,6 +82,6 @@ class MealsController < ApplicationController
def prepare_meals
@quantities = @project.meal_quantities.includes(:formula)
@foods, @requested_n, * = @project.meal_foods.compute_quantities(@quantities)
@foods = @project.meal_foods.compute_quantities(@quantities)
end
end

View File

@ -9,8 +9,13 @@ class MeasurementsController < ApplicationController
before_action :find_project_by_project_id, only: [:index, :new, :create, :filter]
before_action :find_quantity_by_quantity_id, only: [:toggle_exposure]
before_action :find_measurement, only: [:edit, :update, :destroy, :retake]
# @routine is set for :readouts view ONLY
before_action :find_measurement_routine, only: [:readouts, :toggle_exposure]
before_action :find_measurement_routine_by_measurement_routine_id,
only: [:new, :create, :edit, :update, :retake, :filter],
if: -> { params[:view] == 'readouts' }
before_action :authorize
before_action :set_view_params
def index
prepare_measurements
@ -18,7 +23,7 @@ class MeasurementsController < ApplicationController
def new
@measurement = @project.measurements.new
@routine = @measurement.build_routine
@measurement.build_routine
@measurement.readouts.new
end
@ -31,10 +36,10 @@ class MeasurementsController < ApplicationController
update_routine_from_params
@measurement.attributes = measurement_params
@measurement.routine.project = @project
@routine = @measurement.routine
if @measurement.save
if @routine.readout_exposures.empty?
@routine.quantities << @measurement.readouts.map(&:quantity).first(6)
routine = @measurement.routine
if routine.readout_exposures.empty?
routine.quantities << @measurement.readouts.map(&:quantity).first(6)
end
flash[:notice] = 'Created new measurement'
@ -52,7 +57,7 @@ class MeasurementsController < ApplicationController
update_routine_from_params
if @measurement.update(measurement_params)
flash[:notice] = 'Updated measurement'
if @measurement.routine.previous_changes.has_key?(:name) && params[:view] == 'readouts'
if @measurement.routine.previous_changes.has_key?(:name) && @routine
render js: "window.location.pathname='#{readouts_measurement_routine_path(@routine)}'"
else
prepare_items
@ -126,19 +131,26 @@ class MeasurementsController < ApplicationController
end
def prepare_items
params[:view] == 'index' ? prepare_measurements : prepare_readouts
@routine ? prepare_readouts : prepare_measurements
end
def prepare_measurements
@measurements, @filter_q = @project.measurements
.includes(:routine, :source, :readouts)
@measurements, @filter_q = @project.measurements.includes(:routine, :source, :readouts)
.filter(session[:m_filters])
end
def prepare_readouts
@quantities = @routine.quantities.includes(:formula)
@measurements, @requested_r, @extra_r, @filter_q = @routine.measurements
.includes(:routine, :source)
@measurements, @filter_q = @routine.measurements.includes(:routine, :source)
.filter(session[:m_filters], @quantities)
end
def set_view_params
@view_params =
if @routine
{view: :readouts, measurement_routine_id: @routine.id}
else
{view: :index}
end
end
end

View File

@ -17,10 +17,10 @@ module MeasurementsHelper
end
end
def action_links(m, view)
link_to(l(:button_retake), retake_measurement_path(m, view: view),
def action_links(m)
link_to(l(:button_retake), retake_measurement_path(m, @view_params),
{remote: true, class: "icon icon-reload"}) +
link_to(l(:button_edit), edit_measurement_path(m, view: view),
link_to(l(:button_edit), edit_measurement_path(m, @view_params),
{remote: true, class: "icon icon-edit"}) +
delete_link(measurement_path(m), {remote: true, data: {}})
end

View File

@ -3,8 +3,8 @@
<div>
<%= error_messages_for @filter_q %>
<%= form_tag url, id: 'filters-form', name: 'filters-form',
method: :get, remote: true do %>
<%= form_tag url, id: 'filters-form', name: 'filters-form', method: :get,
remote: true do %>
<table class="filter">
<tr>

View File

@ -28,51 +28,52 @@
</thead>
<tbody>
<% @foods.each_with_index do |f, index| %>
<% row_class = "food#{' hidden' if f.hidden} #{cycle('odd', 'even')}" %>
<tr id="food-<%= f.id %>" class="primary <%= row_class %>">
<% extra_quantities = @foods.values.first.keys - @quantities %>
<% @foods.each do |food, nutrients| %>
<% row_class = "food#{' hidden' if food.hidden} #{cycle('odd', 'even')}" %>
<tr id="food-<%= food.id %>" class="primary <%= row_class %>">
<td class="name ellipsible" style="cursor: pointer;"
onclick="$(this).closest('tr').toggle(); $(this).closest('tr').nextUntil('tr.primary', '.food').toggle(); return false;">
<span class="icon icon-bullet-closed"><%= f.name %></span>
<span class="icon icon-bullet-closed"><%= food.name %></span>
</td>
<% @requested_n[index].each do |*, value| %>
<td class="primary value ellipsible"><%= format_value(value) %></td>
<% @quantities.each do |q| %>
<td class="primary value ellipsible"><%= format_value(nutrients[q]) %></td>
<% end %>
<td class="action unwrappable"><%= action_links(f, :nutrients) %></td>
<td class="action unwrappable"><%= action_links(food, :nutrients) %></td>
</tr>
<tr class="<%= row_class %>" style="display:none">
<% if @quantities.length > 0
rows = (@extra_n[index].length - 1) / @quantities.length + 2
rows = (nutrients.length - 1) / @quantities.length + 1
else
rows = 1
end %>
<td rowspan="<%= rows %>" class="name ellipsible" style="cursor: pointer;"
onclick="$(this).closest('tr').prev('tr.primary').toggle(); $(this).closest('tr').prev('tr.primary').nextUntil('tr.primary', '.food').toggle(); return false;">
<span class="icon icon-bullet-open"><%= f.name %></span>
<span class="icon icon-bullet-open"><%= food.name %></span>
</td>
<% @requested_n[index].each do |q, value| %>
<% @quantities.each do |q| %>
<td class="primary quantity ellipsible">
<%= q.name %>
<p class="value"><%= format_value(value) %></p>
<p class="value"><%= format_value(nutrients[q]) %></p>
</td>
<% end %>
<td rowspan="<%= rows %>" class="action unwrappable">
<%= action_links(f, :nutrients) %>
<%= action_links(food, :nutrients) %>
</td>
</tr>
<% next unless @quantities.length > 0 %>
<% @extra_n[index].each_slice(@quantities.length) do |values| %>
<% extra_quantities.each_slice(@quantities.length) do |eqs| %>
<tr class="extra <%= row_class %>" style="display:none">
<% values.each do |q, value| %>
<% eqs.each do |q| %>
<td class="extra quantity ellipsible">
<%= q.name if value %>
<p class="value"><%= format_value(value) %></p>
<%= q.name if nutrients[q] %>
<p class="value"><%= format_value(nutrients[q]) %></p>
</td>
<% end %>
<% if @quantities.length > values.length %>
<td class="space" colspan="<%= @quantities.length-values.length %>"></td>
<% if @quantities.length > eqs.length %>
<td class="space" colspan="<%= @quantities.length - eqs.length %>"></td>
<% end %>
</tr>
<% end %>

View File

@ -1,5 +1,5 @@
<div class="tabular">
<%= fields_for 'measurement[routine_attributes]', @routine do |ff| %>
<%= fields_for 'measurement[routine_attributes]', routine do |ff| %>
<%= ff.hidden_field :id %>
<p>
<label><%= l(:field_name) %><span class="required"> *</span></label>

View File

@ -1,10 +1,9 @@
<div>
<%= fields_for 'measurement[routine_attributes]', @routine do |ff| %>
<%= fields_for 'measurement[routine_attributes]', routine do |ff| %>
<p>
<label><%= l(:field_measurement_routine) %><span class="required"> *</span></label>
<%= ff.select :id,
options_from_collection_for_select(@project.measurement_routines,
:id, :name, @routine.id),
options_from_collection_for_select(@project.measurement_routines, :id, :name, routine.id),
{required: true}, autocomplete: 'off',
onchange: "var mr_id = $('#measurement_routine_attributes_id').val();
$.ajax({
@ -22,7 +21,7 @@
class: 'icon icon-edit' %>
</p>
<% end %>
<% unless @routine.description.empty? %>
<p style='white-space: pre-wrap;' ><%= @routine.description %></p>
<% unless routine.description.empty? %>
<p style='white-space: pre-wrap;' ><%= routine.description %></p>
<% end %>
</div>

View File

@ -1 +1,2 @@
$('#measurement-routine-form').html('<%= j render partial: 'measurement_routines/form' %>');
$('#measurement-routine-form').html('<%= j render partial: 'measurement_routines/form',
locals: {routine: @routine} %>');

View File

@ -1 +1,2 @@
$('#measurement-routine-form').html('<%= j render partial: 'measurement_routines/show_form' %>');
$('#measurement-routine-form').html('<%= j render partial: 'measurement_routines/show_form',
locals: {routine: @routine} %>');

View File

@ -1,4 +1,5 @@
<% if User.current.allowed_to?(:manage_common, @project) %>
<%= link_to t(".link_new_measurement"), new_project_measurement_path(@project, view: view),
<%= link_to t(".link_new_measurement"),
new_project_measurement_path(@project, @view_params),
{remote: true, class: 'icon icon-add'} %>
<% end %>

View File

@ -1,5 +1,5 @@
<%= labelled_form_for @measurement,
url: measurement_path(@measurement, view: view),
url: measurement_path(@measurement, @view_params),
method: :patch, remote: true,
html: {id: 'edit-measurement-form', name: 'edit-measurement-form'} do |f| %>

View File

@ -3,8 +3,8 @@
<div>
<%= error_messages_for @filter_q %>
<%= form_tag url, id: 'filters-form', name: 'filters-form',
method: :get, remote: true do %>
<%= form_tag url, id: 'filters-form', name: 'filters-form', method: :get,
remote: true do %>
<table class="filter">
<tr>

View File

@ -2,10 +2,12 @@
<div class="box">
<div id='measurement-routine-form' class="tabular">
<% if @routine.persisted? %>
<%= render partial: 'measurement_routines/show_form', locals: {f: f} %>
<% if @measurement.routine.persisted? %>
<%= render partial: 'measurement_routines/show_form',
locals: {routine: @measurement.routine} %>
<% else %>
<%= render partial: 'measurement_routines/form', locals: {f: f} %>
<%= render partial: 'measurement_routines/form',
locals: {routine: @measurement.routine} %>
<% end %>
</div>
<hr style="width: 95%;">

View File

@ -1,5 +1,5 @@
<%= render partial: 'measurements/filters',
locals: {url: filter_project_measurements_path(@project, view: :index)} %>
locals: {url: filter_project_measurements_path(@project, @view_params)} %>
<% if @measurements.any? { |m| m.persisted? } %>
<%= error_messages_for @filter_q.formula if @filter_q %>
@ -29,7 +29,7 @@
</td>
<td class="notes ellipsible"><%= m.notes %></td>
<td class="source"><%= m.source.name if m.source.present? %></td>
<td class="action unwrappable"><%= action_links(m, :index) %></td>
<td class="action unwrappable"><%= action_links(m) %></td>
</tr>
<% end %>
</tbody>

View File

@ -1,7 +1,7 @@
<h2><%= t ".heading_new_measurement" %></h2>
<%= labelled_form_for @measurement,
url: project_measurements_path(@project, view: view),
url: project_measurements_path(@project, @view_params),
remote: true,
html: {id: 'new-measurement-form', name: 'new-measurement-form'} do |f| %>

View File

@ -1,7 +1,7 @@
<fieldset id="options" class="collapsible">
<legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
<div>
<%= form_tag toggle_exposure_measurement_routine_path(@routine),
<%= form_tag toggle_exposure_measurement_routine_path(@routine, @view_params),
id: 'toggle-exposure-form', name: 'toggle-exposure-form',
method: :post, remote: true do %>

View File

@ -1,5 +1,5 @@
<%= render partial: 'measurements/filters',
locals: {url: filter_project_measurements_path(@project, view: :readouts)} %>
locals: {url: filter_project_measurements_path(@project, @view_params)} %>
<% if @measurements.any? %>
<%= render partial: 'measurements/options' %>
@ -28,51 +28,52 @@
</thead>
<tbody>
<% @measurements.each_with_index do |m, index| %>
<% extra_quantities = @measurements.values.first.keys - @quantities %>
<% @measurements.each do |measurement, readouts| %>
<% row_class = "measurement #{cycle('odd', 'even')}" %>
<tr id="measurement-<%= m.id %>" class="primary <%= row_class %>">
<tr id="measurement-<%= measurement.id %>" class="primary <%= row_class %>">
<td class="date unwrappable" style="cursor: pointer;"
onclick="$(this).closest('tr').toggle(); $(this).closest('tr').nextUntil('tr.primary', '.measurement').toggle(); return false;">
<span class="icon icon-bullet-closed"><%= format_datetime(m) %></span>
<span class="icon icon-bullet-closed"><%= format_datetime(measurement) %></span>
</td>
<% @requested_r[index].each do |*, value| %>
<td class="primary value ellipsible"><%= format_value(value) %></td>
<% @quantities.each do |q| %>
<td class="primary value ellipsible"><%= format_value(readouts[q]) %></td>
<% end %>
<td class="action unwrappable"><%= action_links(m, :readouts) %></td>
<td class="action unwrappable"><%= action_links(measurement) %></td>
</tr>
<tr class="<%= row_class %>" style="display:none">
<% if @quantities.length > 0
rows = (@extra_r[index].length - 1) / @quantities.length + 2
rows = (readouts.length - 1) / @quantities.length + 1
else
rows = 1
end %>
<td rowspan="<%= rows %>" class="date unwrappable" style="cursor: pointer;"
onclick="$(this).closest('tr').prev('tr.primary').toggle(); $(this).closest('tr').prev('tr.primary').nextUntil('tr.primary', '.measurement').toggle(); return false;">
<span class="icon icon-bullet-open"><%= format_datetime(m) %></span>
<span class="icon icon-bullet-open"><%= format_datetime(measurement) %></span>
</td>
<% @requested_r[index].each do |q, value| %>
<% @quantities.each do |q| %>
<td class="primary quantity ellipsible">
<%= q.name %>
<p class="value"><%= format_value(value) %></p>
<p class="value"><%= format_value(readouts[q]) %></p>
</td>
<% end %>
<td rowspan="<%= rows %>" class="action unwrappable">
<%= action_links(m, :readouts) %>
<%= action_links(measurement) %>
</td>
</tr>
<% next unless @quantities.length > 0 %>
<% @extra_r[index].each_slice(@quantities.length).with_index do |values| %>
<% extra_quantities.each_slice(@quantities.length) do |eqs| %>
<tr class="extra <%= row_class %>" style="display:none">
<% values.each do |q, value| %>
<% eqs.each do |q| %>
<td class="extra quantity ellipsible">
<%= q.name if value %>
<p class="value"><%= format_value(value) %></p>
<%= q.name if readouts[q] %>
<p class="value"><%= format_value(readouts[q]) %></p>
</td>
<% end %>
<% if @quantities.length > values.length %>
<td class="space" colspan="<%= @quantities.length-values.length %>"></td>
<% if @quantities.length > eqs.length %>
<td class="space" colspan="<%= @quantities.length - eqs.length %>"></td>
<% end %>
</tr>
<% end %>

View File

@ -1,5 +1,5 @@
$('#new-measurement').empty();
<% if params[:view] == 'index' %>
<% if @view_params[:view] == :index %>
$('#measurements').html('<%= j render partial: 'measurements/index' %>');
<% else %>
$('#readouts').html('<%= j render partial: 'measurements/readouts' %>');

View File

@ -3,6 +3,6 @@ $('tr[id=measurement-<%= @measurement.id %>]').nextUntil('tr.primary', ':not(.me
var columns = $('table > thead > tr > th').length;
$('tr[id=measurement-<%= @measurement.id %>]').nextUntil('tr.primary').addBack().last().after(
'<tr><td class="form" colspan="'+columns+'"><div id="edit-measurement">' +
'<%= j render partial: 'measurements/edit_form', locals: {view: params[:view]} %>' +
'<%= j render partial: 'measurements/edit_form' %>' +
'</div></td></tr>'
);

View File

@ -1,5 +1,5 @@
<div class="contextual">
<%= render partial: 'measurements/contextual', locals: {view: :index} %>
<%= render partial: 'measurements/contextual' %>
</div>
<div id="new-measurement">

View File

@ -1,4 +1,4 @@
<% if params[:view] == 'index' %>
<% if @view_params[:view] == :index %>
$('#measurements').html('<%= j render partial: 'measurements/index' %>');
<% else %>
$('#readouts').html('<%= j render partial: 'measurements/readouts' %>');

View File

@ -1,2 +1 @@
$('#new-measurement')
.html('<%= j render partial: 'measurements/new_form', locals: {view: params[:view]} %>');
$('#new-measurement').html('<%= j render partial: 'measurements/new_form' %>');

View File

@ -1,5 +1,5 @@
<div class="contextual">
<%= render partial: 'measurements/contextual', locals: {view: :readouts} %>
<%= render partial: 'measurements/contextual' %>
</div>
<div id="new-measurement">

View File

@ -1,8 +1,8 @@
<fieldset id="filters" class="collapsible">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div>
<%= form_tag url, id: 'filters-form', name: 'filters-form',
method: :get, remote: true do %>
<%= form_tag url, id: 'filters-form', name: 'filters-form', method: :get,
remote: true do %>
<table class="filter">
<tr>

View File

@ -27,7 +27,7 @@ module BodyTracking
end
filter_q =
if filters[:formula].present?
if filters[:formula][:code].present?
owner = proxy_association.owner
project = owner.is_a?(Project) ? owner : owner.project
domain = RELATIONS[proxy_association.klass.name][:domain]
@ -43,11 +43,11 @@ module BodyTracking
result =
if requested_q || apply_formula
computed = items.compute_quantities(requested_q, apply_formula && filter_q)
requested_q ? computed : [computed[0]]
requested_q ? computed : computed.keys
else
[items]
items
end
result.push(filter_q)
[result, filter_q]
end
def compute_quantities(requested_q, filter_q = nil)
@ -61,12 +61,11 @@ module BodyTracking
relations[:subitem_class].where(relations[:association] => items)
.includes(:quantity, :unit).order('quantities.lft').each do |s|
item = s.send(relations[:association])
subitem_value = s.send(relations[:value_field])
subitems[s.quantity][s.send(relations[:association])] = [subitem_value, s.unit]
subitems[s.quantity][item] = [subitem_value, s.unit]
end
extra_q = subitems.keys - requested_q
completed_q = {}
# FIXME: loop should finish unless there is circular dependency in formulas
# for now we don't guard against that
@ -78,7 +77,6 @@ module BodyTracking
if !q.formula || q.formula.errors.any? || !q.formula.valid? ||
(subitems[q].length == items.count)
completed_q[q] = subitems.delete(q) { {} }
completed_q[q].default = [nil, nil]
next
end
@ -94,7 +92,7 @@ module BodyTracking
output_items = items.select { |i| subitems[q][i].nil? }
input_q = q.formula.quantities
inputs = input_q.map do |i_q|
values = completed_q[i_q].values_at(*output_items)
values = completed_q[i_q].values_at(*output_items).map { |v| v || [nil, nil] }
values.map! { |v, u| [v || BigDecimal(0), u] } if q.formula.zero_nil
[i_q, values]
end
@ -121,12 +119,11 @@ module BodyTracking
unchecked_q << [q, deps]
end
all_q = subitems.merge(completed_q)
[
filter_q ? items.to_a.keep_if { |i| all_q[filter_q][i][0] } : items,
items.map { |i| requested_q.map { |q| [q, all_q[q][i]] } },
items.map { |i| extra_q.map { |q| [q, all_q[q][i]] } }
]
filter_values = completed_q.delete(filter_q)
items.to_a.keep_if { |i| filter_values[i][0] } if filter_values
subitems.merge!(completed_q)
subitem_keys = subitems.keys.sort_by { |q| q.lft }
items.map { |i| [i, subitem_keys.map { |q| [q, subitems[q][i]] }.to_h] }.to_h
end
end
end