Class: Pacing::Pacer
- Inherits:
-
Object
- Object
- Pacing::Pacer
- Defined in:
- lib/pacing/pacer.rb
Constant Summary collapse
- COMMON_YEAR_DAYS_IN_MONTH =
[nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
Instance Attribute Summary collapse
-
#date ⇒ Object
readonly
Returns the value of attribute date.
-
#interval ⇒ Object
readonly
Returns the value of attribute interval.
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
-
#non_business_days ⇒ Object
readonly
Returns the value of attribute non_business_days.
-
#school_plan ⇒ Object
readonly
Returns the value of attribute school_plan.
-
#show_frequency ⇒ Object
readonly
Returns the value of attribute show_frequency.
-
#state ⇒ Object
readonly
Returns the value of attribute state.
-
#summer_holidays ⇒ Object
readonly
Returns the value of attribute summer_holidays.
Instance Method Summary collapse
-
#business_days(start_date, end_date) ⇒ Object
days on which a session can hold.
- #calculate ⇒ Object
- #date_within_range ⇒ Object
-
#discipline_frequency(service) ⇒ Object
discipline iep frequency.
- #end_of_treatment_date(reset_start, interval) ⇒ Object
-
#expected_visits(start_date:, end_date:, frequency:, interval:) ⇒ Object
get a spreadout of visit dates over an interval by using simple proportion.
- #get_holidays(start_date, end_date) ⇒ Object
- #get_working_days(start_date, end_date) ⇒ Object
-
#initialize(school_plan:, date:, non_business_days:, state: :us_tn, mode: :liberal, summer_holidays: [], show_frequency: false) ⇒ Pacer
constructor
A new instance of Pacer.
- #interval_days(interval) ⇒ Object
- #pace(actual_visits, expected_visits) ⇒ Object
- #pace_indicator(pace) ⇒ Object
- #parse_date(date) ⇒ Object
- #parse_summer_holiday_dates ⇒ Object
- #parsed_non_business_days ⇒ Object
- #readable_suggestion(rate:) ⇒ Object
- #remaining_days(start_date:, interval:) ⇒ Object
-
#remaining_visits(completed_visits:, required_visits:) ⇒ Object
scoped to the interval.
-
#reset_date(start_date:, interval:) ⇒ Object
scoped to the interval.
-
#reset_date_monthly(start_date, interval) ⇒ Object
reset date for the monthly interval.
-
#reset_date_weekly(start_date, interval) ⇒ Object
reset date for the weekly interval.
-
#reset_date_yearly(start_date) ⇒ Object
reset date for the yearly interval.
-
#start_of_treatment_date(start_date, interval = "monthly") ⇒ Object
scoped to the interval.
-
#start_of_treatment_date_monthly(start_date) ⇒ Object
start of treatment for the monthly interval.
-
#start_of_treatment_date_weekly(start_date) ⇒ Object
start of treatment for the weekly interval.
-
#start_of_treatment_date_yearly(start_date) ⇒ Object
start of treatment for the yearly interval.
- #suggested_rate(remaining_visits:, start_date:, interval:) ⇒ Object
-
#week_start(date, offset_from_sunday = 0) ⇒ Object
get actual date of the first day of the week where date falls.
Constructor Details
#initialize(school_plan:, date:, non_business_days:, state: :us_tn, mode: :liberal, summer_holidays: [], show_frequency: false) ⇒ Pacer
Returns a new instance of Pacer.
9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/pacing/pacer.rb', line 9 def initialize(school_plan:, date:, non_business_days:, state: :us_tn, mode: :liberal, summer_holidays: [], show_frequency: false) @school_plan = school_plan @non_business_days = non_business_days @date = date @state = state @show_frequency = show_frequency @mode = [:strict, :liberal].include?(mode) ? mode : :liberal Pacing::Error.new(school_plan: school_plan, date: date, non_business_days: non_business_days, state: state, mode: mode, summer_holidays: summer_holidays) @summer_holidays = summer_holidays.empty? ? parse_summer_holiday_dates : [parse_date(summer_holidays[0]), parse_date(summer_holidays[1])] end |
Instance Attribute Details
#date ⇒ Object (readonly)
Returns the value of attribute date.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def date @date end |
#interval ⇒ Object (readonly)
Returns the value of attribute interval.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def interval @interval end |
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def mode @mode end |
#non_business_days ⇒ Object (readonly)
Returns the value of attribute non_business_days.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def non_business_days @non_business_days end |
#school_plan ⇒ Object (readonly)
Returns the value of attribute school_plan.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def school_plan @school_plan end |
#show_frequency ⇒ Object (readonly)
Returns the value of attribute show_frequency.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def show_frequency @show_frequency end |
#state ⇒ Object (readonly)
Returns the value of attribute state.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def state @state end |
#summer_holidays ⇒ Object (readonly)
Returns the value of attribute summer_holidays.
7 8 9 |
# File 'lib/pacing/pacer.rb', line 7 def summer_holidays @summer_holidays end |
Instance Method Details
#business_days(start_date, end_date) ⇒ Object
days on which a session can hold
172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/pacing/pacer.rb', line 172 def business_days(start_date, end_date) # remove saturdays and sundays # remove holidays(array from Ambiki) # remove school holidays/non business days # should we remove today?(datetime call?) summer = get_working_days(summer_holidays[0], summer_holidays[1]) working_days = get_working_days(start_date, end_date) holidays = get_holidays(start_date, end_date) working_days.sort - parsed_non_business_days.sort - holidays.sort - summer.sort end |
#calculate ⇒ Object
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/pacing/pacer.rb', line 54 def calculate # filter out services that haven't started or whose time is passed 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 within end services = services.map do |service| discipline = {} expected = expected_visits(start_date: parse_date(service[:start_date]), end_date: parse_date(service[:end_date]), frequency: service[:frequency], interval: service[:interval]) discipline[:pace] = pace(service[:completed_visits_for_current_interval], expected) discipline[:reset_date] = reset_date(start_date: service[:start_date], interval: service[:interval]) discipline[:reset_date] = parse_date(discipline[:reset_date]) > parse_date(service[:end_date]) && service[:interval] == "yearly" ? service[:end_date] : discipline[:reset_date] discipline[:remaining_visits] = remaining_visits(completed_visits: service[:completed_visits_for_current_interval], required_visits: service[:frequency] + service[:extra_sessions_allowable]) discipline[:pace_indicator] = pace_indicator(discipline[:pace]) discipline[:used_visits] = service[:completed_visits_for_current_interval] 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[:discipline] = service[:type_of_service] discipline[:pace_indicator] = pace_indicator(discipline[:pace]) discipline[:pace_suggestion] = readable_suggestion(rate: discipline[:suggested_rate]) if show_frequency discipline[:frequency] = discipline_frequency(service) end discipline.delete(:suggested_rate) discipline end services end |
#date_within_range ⇒ Object
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/pacing/pacer.rb', line 294 def date_within_range valid_range_or_exceptions = false begin @school_plan[:school_plan_services].each do |school_plan_service| if (parse_date(school_plan_service[:start_date]) <= parse_date(@date) && parse_date(@date) <= parse_date(school_plan_service[:end_date])) valid_range_or_exceptions = true end end rescue => exception valid_range_or_exceptions = true end valid_range_or_exceptions end |
#discipline_frequency(service) ⇒ Object
discipline iep frequency
105 106 107 |
# File 'lib/pacing/pacer.rb', line 105 def discipline_frequency(service) service[:frequency].to_s + service[:interval].gsub(/(per)|p/i, "").strip[0].upcase + "x" + service[:time_per_session_in_minutes].to_s + service[:type_of_service][0].upcase + "T" end |
#end_of_treatment_date(reset_start, interval) ⇒ Object
274 275 276 |
# File 'lib/pacing/pacer.rb', line 274 def end_of_treatment_date(reset_start, interval) reset_start + interval_days(interval) end |
#expected_visits(start_date:, end_date:, frequency:, interval:) ⇒ Object
get a spreadout of visit dates over an interval by using simple proportion.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/pacing/pacer.rb', line 110 def expected_visits(start_date:, end_date:, frequency:, interval:) reset_start = start_of_treatment_date(start_date, interval) reset_end = end_of_treatment_date(reset_start, interval) days_between = business_days(reset_start, reset_end).count days_passed = 0 visits = 0 if parse_date(@date) > reset_start days_passed = business_days(reset_start, parse_date(@date)).count end if days_between != 0 visits = ((frequency/days_between.to_f) * days_passed).round end return visits end |
#get_holidays(start_date, end_date) ⇒ Object
223 224 225 226 227 228 |
# File 'lib/pacing/pacer.rb', line 223 def get_holidays(start_date, end_date) begin Holidays.between(start_date, end_date, @state).map { |holiday| holiday[:date] } rescue => exception end end |
#get_working_days(start_date, end_date) ⇒ Object
217 218 219 220 221 |
# File 'lib/pacing/pacer.rb', line 217 def get_working_days(start_date, end_date) (start_date..end_date).filter do |day| !(day.saturday? || day.sunday?) end end |
#interval_days(interval) ⇒ Object
132 133 134 135 136 137 138 139 140 141 |
# File 'lib/pacing/pacer.rb', line 132 def interval_days(interval) case interval when "monthly", "per month" return COMMON_YEAR_DAYS_IN_MONTH[(parse_date(@date)).month] when "weekly", "per week" return 6 when "yearly", "per year" return parse_date(@date).leap? ? 366 : 365 end end |
#pace(actual_visits, expected_visits) ⇒ Object
143 144 145 146 |
# File 'lib/pacing/pacer.rb', line 143 def pace(actual_visits, expected_visits) # Pace = actual visits at point in time - expected visits to meet frequency at that point in time actual_visits - expected_visits end |
#pace_indicator(pace) ⇒ Object
148 149 150 151 152 153 154 155 156 |
# File 'lib/pacing/pacer.rb', line 148 def pace_indicator(pace) if pace == 0 return "😁" elsif pace > 0 return "🐇" elsif pace < 0 return "🐢" end end |
#parse_date(date) ⇒ Object
158 159 160 161 162 163 |
# File 'lib/pacing/pacer.rb', line 158 def parse_date(date) begin Date.strptime(date, '%m-%d-%Y') rescue => exception end end |
#parse_summer_holiday_dates ⇒ Object
345 346 347 348 349 350 351 352 353 |
# File 'lib/pacing/pacer.rb', line 345 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 |
#parsed_non_business_days ⇒ Object
165 166 167 168 169 |
# File 'lib/pacing/pacer.rb', line 165 def parsed_non_business_days @non_business_days.map do |day| parse_date(day) end end |
#readable_suggestion(rate:) ⇒ Object
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/pacing/pacer.rb', line 310 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' elsif rate >= 0.2 && rate < 0.25 'once a week' elsif rate >= 0.25 && rate < 0.33 'once every 4 days' elsif rate >= 0.33 && rate < 0.5 'once every 3 days' elsif rate == 0.5 'once every other day' elsif rate > 0.5 && rate < 1 'about once every other day' elsif rate >= 1.00 'once a day' end end |
#remaining_days(start_date:, interval:) ⇒ Object
336 337 338 339 340 341 342 343 |
# File 'lib/pacing/pacer.rb', line 336 def remaining_days(start_date:, interval:) reset_start = start_of_treatment_date(start_date, interval) reset_end = end_of_treatment_date(reset_start, interval) days_left = business_days(parse_date(@date), reset_end).count days_left end |
#remaining_visits(completed_visits:, required_visits:) ⇒ Object
scoped to the interval
187 188 189 190 191 |
# File 'lib/pacing/pacer.rb', line 187 def remaining_visits(completed_visits:, required_visits:) visits = required_visits.to_i - completed_visits.to_i visits = 0 if visits < 0 visits end |
#reset_date(start_date:, interval:) ⇒ Object
scoped to the interval
194 195 196 197 198 199 200 201 202 203 |
# File 'lib/pacing/pacer.rb', line 194 def reset_date(start_date:, interval:) case interval when "monthly", "per month" return reset_date_monthly(start_date, interval) when "weekly", "per week" return reset_date_weekly(start_date, interval) when "yearly", "per year" return reset_date_yearly(start_date) end end |
#reset_date_monthly(start_date, interval) ⇒ Object
reset date for the monthly interval
243 244 245 246 247 |
# File 'lib/pacing/pacer.rb', line 243 def reset_date_monthly(start_date, interval) 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_weekly(start_date, interval) ⇒ Object
reset date for the weekly interval
250 251 252 |
# File 'lib/pacing/pacer.rb', line 250 def reset_date_weekly(start_date, interval) (start_of_treatment_date(parse_date(start_date), interval) + 7).strftime("%m-%d-%Y") end |
#reset_date_yearly(start_date) ⇒ Object
reset date for the yearly interval
238 239 240 |
# File 'lib/pacing/pacer.rb', line 238 def reset_date_yearly(start_date) (parse_date(@date).leap? ? parse_date(start_date) + 366 : parse_date(start_date) + 365).strftime("%m-%d-%Y") end |
#start_of_treatment_date(start_date, interval = "monthly") ⇒ Object
scoped to the interval
206 207 208 209 210 211 212 213 214 215 |
# File 'lib/pacing/pacer.rb', line 206 def start_of_treatment_date(start_date, interval="monthly") case interval when "monthly", "per month" return start_of_treatment_date_monthly(start_date) when "weekly", "per week" return start_of_treatment_date_weekly(start_date) when "yearly", "per year" return start_of_treatment_date_yearly(start_date) end end |
#start_of_treatment_date_monthly(start_date) ⇒ Object
start of treatment for the monthly interval
266 267 268 269 270 271 272 |
# File 'lib/pacing/pacer.rb', line 266 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}") end end |
#start_of_treatment_date_weekly(start_date) ⇒ Object
start of treatment for the weekly interval
279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/pacing/pacer.rb', line 279 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 = parsed_date < weekly_date ? weekly_date - 7 : weekly_date end weekly_date end |
#start_of_treatment_date_yearly(start_date) ⇒ Object
start of treatment for the yearly interval
255 256 257 258 259 260 261 262 263 |
# File 'lib/pacing/pacer.rb', line 255 def start_of_treatment_date_yearly(start_date) start = parse_date("#{start_date.month}-#{start_date.day}-#{parse_date(@date).year}") if start > parse_date(date) start = start_date end start # start_date end |
#suggested_rate(remaining_visits:, start_date:, interval:) ⇒ Object
330 331 332 333 334 |
# File 'lib/pacing/pacer.rb', line 330 def suggested_rate(remaining_visits:, start_date:, interval:) days_left = remaining_days(start_date: start_date, interval: interval).to_f days_left = 1 if days_left == 0 (remaining_visits / days_left.to_f).round(2) end |
#week_start(date, offset_from_sunday = 0) ⇒ Object
get actual date of the first day of the week where date falls
231 232 233 234 235 |
# File 'lib/pacing/pacer.rb', line 231 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 |