lib/pacing/pacer.rb in pacing-1.0.1 vs lib/pacing/pacer.rb in pacing-2.0.0
- old
+ new
@@ -1,10 +1,9 @@
require 'date'
require 'holidays'
module Pacing
- # two modes(strict: use start dates strictly in calculating pacing)
class Pacer
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
attr_reader :school_plan, :date, :non_business_days, :state, :mode, :interval, :summer_holidays
def initialize(school_plan:, date:, non_business_days:, state: :us_tn, mode: :liberal, summer_holidays: [])
@@ -12,51 +11,49 @@
@non_business_days = non_business_days
@date = date
@state = state
@mode = [:strict, :liberal].include?(mode) ? mode : :liberal
- raise ArgumentError.new("You must pass in at least one school plan") if @school_plan.nil?
- raise TypeError.new("School plan must be a hash") if @school_plan.class != Hash
+ Pacing::Error.new(school_plan: school_plan, date: date, non_business_days: non_business_days, state: state, mode: mode, summer_holidays: summer_holidays)
- raise ArgumentError.new('You must pass in a date') if @date.nil?
- raise TypeError.new("The date should be formatted as a string in the format mm-dd-yyyy") if @date.class != String || !/(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])-(19|20)\d\d/.match?(@date)
- raise ArgumentError.new('Date must be within the interval range of the school plan') if !date_within_range
+ @summer_holidays = summer_holidays.empty? ? parse_summer_holiday_dates : [parse_date(summer_holidays[0]), parse_date(summer_holidays[1])]
+ end
- @non_business_days.each do |non_business_day|
- raise TypeError.new('"Non business days" dates should be formatted as a string in the format mm-dd-yyyy') if non_business_day.class != String || !/(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])-(19|20)\d\d/.match?(non_business_day)
+ def interval
+ # filter out services that haven't started or whose time is passed
+ services = @school_plan[:school_plan_services].filter do |school_plan_service|
+ within = true
+ if !(parse_date(school_plan_service[:start_date]) <= parse_date(@date) && parse_date(@date) <= parse_date(school_plan_service[:end_date]))
+ within = false
+ end
+
+ within
end
- @school_plan[:school_plan_services].each do |school_plan_service|
- raise TypeError.new("School plan type must be a string and cannot be nil") if school_plan_service[:school_plan_type].class != String || school_plan_service[:school_plan_type].nil?
+ services = services.map do |service|
+ if ["pragmatic language", "speech and language", "language", "speech", "language therapy", "speech therapy", "speech and language therapy", "speech language therapy"].include?(service[:type_of_service].downcase)
+ discipline_name = "Speech Therapy"
+ elsif ["occupation therapy", "occupational therapy"].include?(service[:type_of_service].downcase)
+ discipline_name = "Occupational Therapy"
+ elsif ["physical therapy"].include?(service[:type_of_service].downcase)
+ discipline_name = "Physical Therapy"
+ elsif ["feeding therapy"].include?(service[:type_of_service].downcase)
+ discipline_name = "Feeding Therapy"
+ end
- raise ArgumentError.new("School plan services start and end dates can not be nil") if school_plan_service[:start_date].nil? || school_plan_service[:end_date].nil?
-
- raise TypeError.new("School plan services start and end dates should be formatted as a string in the format mm-dd-yyyy") if school_plan_service[:start_date].class != String || !/(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])-(19|20)\d\d/.match?(school_plan_service[:start_date])
-
- raise TypeError.new("School plan services start and end dates should be formatted as a string in the format mm-dd-yyyy") if school_plan_service[:end_date].class != String || !/(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])-(19|20)\d\d/.match?(school_plan_service[:end_date])
-
- raise TypeError.new("Type of service must be a string and cannot be nil") if school_plan_service[:type_of_service].class != String || school_plan_service[:type_of_service].nil?
-
- raise TypeError.new("Frequency must be an integer and cannot be nil") if school_plan_service[:frequency].class != Integer || school_plan_service[:frequency].nil?
-
- raise TypeError.new("Interval must be a string and cannot be nil") if school_plan_service[:interval].class != String || school_plan_service[:interval].nil?
-
- raise TypeError.new("Time per session in minutes must be an integer and cannot be nil") if school_plan_service[:time_per_session_in_minutes].class != Integer || school_plan_service[:time_per_session_in_minutes].nil?
-
- raise TypeError.new("Completed visits for current interval must be an integer and cannot be nil") if school_plan_service[:completed_visits_for_current_interval].class != Integer || school_plan_service[:completed_visits_for_current_interval].nil?
-
- raise TypeError.new("Extra sessions allowable must be an integer and cannot be nil") if school_plan_service[:extra_sessions_allowable].class != Integer || school_plan_service[:extra_sessions_allowable].nil?
-
- raise TypeError.new("Interval for extra sessions allowable must be a string and cannot be nil") if school_plan_service[:interval_for_extra_sessions_allowable].class != String || school_plan_service[:interval_for_extra_sessions_allowable].nil?
+ discipline = {}
+ discipline[:discipline] = discipline_name
+ discipline[:reset_date] = reset_date(start_date: service[:start_date], interval: service[:interval])
+ discipline[:start_date] = start_of_treatment_date(parse_date(service[:start_date]), service[:interval]).strftime("%m-%d-%Y")
+ discipline
end
-
- @summer_holidays = summer_holidays.empty? ? parse_summer_holiday_dates : [parse_date(summer_holidays[0]), parse_date(summer_holidays[1])]
end
def calculate
# filter out services that haven't started or whose time is passed
- services = @school_plan[:school_plan_services].filter do |school_plan_service|
+ school_plan_services = Pacing::Normalizer.new(@school_plan[:school_plan_services], @date).normalize
+ services = school_plan_services[:school_plan_services].filter do |school_plan_service|
within = true
if !(parse_date(school_plan_service[:start_date]) <= parse_date(@date) && parse_date(@date) <= parse_date(school_plan_service[:end_date]))
within = false
end
@@ -82,16 +79,21 @@
discipline[:suggested_rate] = suggested_rate(remaining_visits: discipline[:remaining_visits], start_date: parse_date(service[:start_date]), interval: service[:interval])
discipline[:expected_visits_at_date] = expected
- discipline[:type_of_service] = service[:type_of_service]
+ discipline[:discipline] = service[:type_of_service]
+ discipline[:pace_indicator] = pace_indicator(discipline[:pace])
+ discipline[:pace_suggestion] = readable_suggestion(rate: discipline[:suggested_rate])
+
+ discipline.delete(:suggested_rate)
+
discipline
end
- disciplines_cleaner ([speech_discipline(services), occupational_discipline(services), physical_discipline(services), feeding_discipline(services)])
+ services
end
# get a spreadout of visit dates over an interval by using simple proportion.
def expected_visits(start_date:, end_date:, frequency:, interval:)
reset_start = start_of_treatment_date(start_date, interval)
@@ -213,10 +215,11 @@
end
end
# get actual date of the first day of the week where date falls
def week_start(date, offset_from_sunday=0)
+ offset_from_sunday = @mode == :liberal ? 1 : 0
return date if date.monday?
date - ((date.wday - offset_from_sunday) % 7)
end
# reset date for the yearly interval
@@ -224,11 +227,13 @@
(parse_date(@date).leap? ? parse_date(start_date) + 366 : parse_date(start_date) + 365).strftime("%m-%d-%Y")
end
# reset date for the monthly interval
def reset_date_monthly(start_date, interval)
- (start_of_treatment_date(parse_date(start_date), interval) + COMMON_YEAR_DAYS_IN_MONTH[(parse_date(@date)).month]).strftime("%m-%d-%Y")
+ month = (parse_date(@date)).month
+
+ (start_of_treatment_date(parse_date(start_date), interval) + COMMON_YEAR_DAYS_IN_MONTH[month]).strftime("%m-%d-%Y")
end
# reset date for the weekly interval
def reset_date_weekly(start_date, interval)
(start_of_treatment_date(parse_date(start_date), interval) + 7).strftime("%m-%d-%Y")
@@ -243,11 +248,11 @@
start
# start_date
end
- # start of treatment for the montly interval
+ # start of treatment for the monthly interval
def start_of_treatment_date_monthly(start_date)
if @mode == :strict
return parse_date("#{parse_date(@date).month}-#{start_date.day}-#{parse_date(@date).year}")
else
return parse_date("#{parse_date(@date).month}-01-#{parse_date(@date).year}")
@@ -258,16 +263,18 @@
reset_start + interval_days(interval)
end
# start of treatment for the weekly interval
def start_of_treatment_date_weekly(start_date)
+ # TODO: Update with assumption that Monday is start of week
+ # Future TODO: allow user to pass in configuration for start of week
parsed_date = parse_date(@date)
week_start_date = week_start(parsed_date)
weekly_date = week_start_date
if week_start_date != parsed_date && @mode == :strict
- weekly_date = week_start_date + start_date.wday #unless start_date.wday == 1
+ weekly_date = week_start_date + start_date.wday # unless start_date.wday == 1
weekly_date = parsed_date < weekly_date ? weekly_date - 7 : weekly_date
end
weekly_date
end
@@ -286,116 +293,10 @@
end
valid_range_or_exceptions
end
- def speech_discipline(services)
- discipline = {
- :discipline => "Speech Therapy",
- :remaining_visits => 0,
- :used_visits => 0,
- :pace => 0,
- :pace_indicator => "🐢",
- :pace_suggestion => "once a day",
- :suggested_rate => 0,
- :expected_visits_at_date => 0,
- :reset_date => nil } # some arbitrarity date in the past
-
- discipline_services = services.filter do |service|
- ["pragmatic language", "speech and language", "language", "speech", "language therapy", "speech therapy", "speech and language therapy", "speech language therapy"].include?(service[:type_of_service].downcase)
- end
-
- return {} if discipline_services.empty?
-
- discipline_data(discipline_services, discipline)
- end
-
- def occupational_discipline(services)
- discipline = {
- :discipline=>"Occupational Therapy",
- :remaining_visits=>0,
- :used_visits=>0,
- :pace=>0,
- :pace_indicator=>"🐢",
- :pace_suggestion=>"once a day",
- :suggested_rate => 0,
- :expected_visits_at_date=>0,
- :reset_date=> nil } # some arbitrarity date in the past
-
- discipline_services = services.filter do |service|
- ["occupation therapy", "occupational therapy"].include?(service[:type_of_service].downcase)
- end
-
- return {} if discipline_services.empty?
-
- discipline_data(discipline_services, discipline)
- end
-
- def physical_discipline(services)
- discipline = {
- :discipline=>"Physical Therapy",
- :remaining_visits=>0,
- :used_visits=>0,
- :pace=>0,
- :pace_indicator=>"🐢",
- :pace_suggestion=>"once a day",
- :suggested_rate => 0,
- :expected_visits_at_date=>0,
- :reset_date=> nil } # some arbitrarity date in the past
-
- discipline_services = services.filter do |service|
- ["physical therapy"].include?(service[:type_of_service].downcase)
- end
-
- return {} if discipline_services.empty?
-
- discipline_data(discipline_services, discipline)
- end
-
- def feeding_discipline(services)
- discipline = {
- :discipline=>"Feeding Therapy",
- :remaining_visits=>0,
- :used_visits=>0,
- :pace=>0,
- :pace_indicator=>"🐢",
- :pace_suggestion=>"once a day",
- :suggested_rate => 0,
- :expected_visits_at_date=>0,
- :reset_date=> nil } # some arbitrarity date in the past
-
- discipline_services = services.filter do |service|
- ["Feeding Therapy"].include? service[:type_of_service]
- end
-
- return {} if discipline_services.empty?
-
- discipline_data(discipline_services, discipline)
- end
-
- def discipline_data(services, discipline)
- services.each do |service|
- discipline[:pace] = discipline[:pace] ? discipline[:pace].to_i + service[:pace].to_i : service[:pace]
-
- discipline[:remaining_visits] = discipline[:remaining_visits] ? discipline[:remaining_visits].to_i + service[:remaining_visits].to_i : service[:remaining_visits]
-
- discipline[:used_visits] = discipline[:used_visits] ? discipline[:used_visits].to_i + service[:used_visits].to_i : service[:used_visits]
-
- discipline[:expected_visits_at_date] = discipline[:expected_visits_at_date] ? discipline[:expected_visits_at_date].to_i + service[:expected_visits_at_date].to_i : service[:expected_visits_at_date]
-
- discipline[:suggested_rate] = discipline[:suggested_rate] ? discipline[:suggested_rate].to_f + service[:suggested_rate].to_f : service[:suggested_rate].to_f
-
- discipline[:reset_date] = (!discipline[:reset_date].nil? && parse_date(service[:reset_date]) < parse_date(discipline[:reset_date])) ? discipline[:reset_date] : service[:reset_date]
- end
-
- discipline[:pace_indicator] = pace_indicator(discipline[:pace])
- discipline[:pace_suggestion] = readable_suggestion(rate: discipline[:suggested_rate])
-
- discipline.delete(:suggested_rate)
- discipline
- end
-
def readable_suggestion(rate:)
# rate = suggested_rate(remaining_visits: remaining_visits, start_date: start_date, interval: interval)
if rate < 0.2
'less than once per week'
@@ -427,22 +328,16 @@
days_left = business_days(parse_date(@date), reset_end).count
days_left
end
- def disciplines_cleaner(disciplines)
- # use the fake arbitrary reset date to remove unrequired disciplines
- disciplines.filter { |discipline| !discipline.empty? }
- end
-
def parse_summer_holiday_dates
holidays_start = parse_date("05-13-#{parse_date(@date).year}")
holidays_start += 1 until holidays_start.wday == 5
holidays_end = parse_date("08-01-#{parse_date(@date).year}")
holidays_start += 1 until holidays_start.wday == 1
[holidays_start, holidays_end]
- end
+ end
end
end
-