houdini/lib/calculate/calculate_suggested_amounts.rb
Bradley M. Kuhn 6772312ea7 Relicense all .rb files under new project license.
The primary license of the project is changing to:
  AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later

with some specific files to be licensed under the one of two licenses:
   CC0-1.0
   LGPL-3.0-or-later

This commit is one of the many steps to relicense the entire codebase.

Documentation granting permission for this relicensing (from all past
contributors who hold copyrights) is on file with Software Freedom
Conservancy, Inc.
2018-03-25 15:10:40 -04:00

93 lines
No EOL
2.6 KiB
Ruby

# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
require 'numeric'
module CalculateSuggestedAmounts
MIN = 25
MAX = 100000000
BRACKETS = [{range: MIN...1000, delta:100},
{range: 1000...5000, delta: 500},
{range: 5000...MAX, delta: 2500}]
# Calculates a set of suggested donation amounts based upon our internal special algorithm
# This is most useful for suggesting amounts a recurring donor could change to
# @return [Array<Integer>] suggested amounts for your donation
# @param [Number] amount the amount in cents to start from
def self.calculate(amount)
ParamValidation.new({amount: amount}, amount: {required:true, is_a: Numeric, min: MIN, max:MAX })
result = []
step_down_val = step_down_value(amount)
unless step_down_val.nil?
result.push(step_down_val)
end
higher_amounts = []
while (higher_amounts.empty? || (higher_amounts.length < 3 && higher_amounts.last() != nil))
if higher_amounts.empty?
higher_amounts.push(step_up_value(amount))
else
higher_amounts.push(step_up_value(higher_amounts.last))
end
end
result.concat(higher_amounts.reject {|i| i.nil?})
end
def self.step_down_value(amount)
initial_bracket = get_bracket_by_amount(amount)
#check_floor_for_delta
delta_floor = amount.floor_for_delta(initial_bracket[:delta])
#not on a delta, just send a floor
if (delta_floor != amount)
return delta_floor < MIN ? nil : delta_floor
end
potential_lower_amount = amount - initial_bracket[:delta]
# is potential_lower_amount < our MIN? if so, return nil
return nil if potential_lower_amount < MIN
new_bracket = get_bracket_by_amount(potential_lower_amount)
#if in same bracket, potential_lower_amount is our step_down_value
if initial_bracket == new_bracket
return potential_lower_amount
end
#we're going to step down by our new bracket value then
return amount - new_bracket[:delta]
end
def self.step_up_value(amount)
bracket = get_bracket_by_amount(amount)
#check_ceil_for_delta
delta_ceil = amount.ceil_for_delta(bracket[:delta])
#not on a delta, just send a ceil
if (delta_ceil != amount)
return delta_ceil >= MAX ? nil : delta_ceil
end
potential_higher_amount = amount + bracket[:delta]
# is potential_lower_amount < our MIN? if so, return nil
return nil if potential_higher_amount >= MAX
potential_higher_amount
end
def self.get_bracket_by_amount(amount)
BRACKETS.select { |i| i[:range].cover?(amount) }.first
end
end