class TargetNutrientCalculator # Derive nutrient columns from the NutrientMeasurement table NUTRIENT_COLUMNS = (NutrientMeasurement.column_names - %w[id measured_on created_at updated_at]) .map!(&:to_sym) .freeze # Returns an unsaved NutrientMeasurement with target concentrations (e.g., mg/L) def self.call rafts = Raft.includes(:crop).where.not(crop_id: nil) total = rafts.count return empty_measurement if total.zero? sums = Hash.new(0.0) rafts.each do |raft| NUTRIENT_COLUMNS.each do |col| v = raft.crop.public_send(col) sums[col] += v.to_f if v end end targets = sums.transform_values { |s| s / total } NutrientMeasurement.new({ measured_on: Date.current }.merge(targets)) end private def empty_measurement NutrientMeasurement.new( { measured_on: Date.current }.merge(NUTRIENT_COLUMNS.index_with { 0.0 }) ) end end