pacing 1.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51308fae8355d11876362b5f8079e2ce3ba4f9895cd84f3fd3f72758a8527a47
4
- data.tar.gz: 86235e9e944f0250cb22d67da1483b30b793e0961539556a5b79f152a5b9ea6b
3
+ metadata.gz: bbc2189e33671c8a34eccd7aa71ec4f93d48fe0228e4931655bda84c519cc1d8
4
+ data.tar.gz: a1808e5be13be267c73b2720fabafbfe9e379029a209a86f5b19921c515497f5
5
5
  SHA512:
6
- metadata.gz: 3bb5310571685c41aa07daa78dfe72272498eda87a01239165692c9ced4a02bd3a4c0afb7b7748f67b6d804756b4cfbdc9d9f55b6c38171dac49eda7c2a3f818
7
- data.tar.gz: 58758425a4e180e61b7c47ecac9dec3f2a9e685b37a47d46514a74951f18b5fd87839d05fc46cbef04b40da0c85e93e80853cdc31933917e413d2dc2b285716a
6
+ metadata.gz: f320cddb2b0a54b1f834c4a8a98fb496f6d7348c848525c26fdd52e22eaec8312f083df2b9378ca0bb2794480f6d6c3e8b65239221bdd27cf5463ac5bb8f2080
7
+ data.tar.gz: 7a6d01438e8ab61f2bec040cd2ddbdd14fda5b832d93324678ce0852fb10e34cd9a8d608b4c58fc89987d974c40a9251abbc4b9a25bb6c50b0c46a68e6688055
data/README.md CHANGED
@@ -116,8 +116,62 @@ paced.calculate
116
116
  }
117
117
  ]
118
118
  =end
119
+
120
+ # Optionally if we want to include the frequency of the discipline in the pacing output we can
121
+ # pass in the optional param `show_frequency`. This value defaults to false.
122
+
123
+ paced = Pacing::Pacer.new(school_plan: school_plan, date: date, non_business_days: non_business_days, mode: :liberal, summer_holidays: summer_holidays, state: state, show_frequency: true)
124
+ paced.calculate
125
+
126
+ # Below is the output we will get when in strict mode and also showing the frequency in the pacing output.
127
+ =begin
128
+ => [
129
+ {
130
+ discipline: 'Speech Therapy',
131
+ remaining_visits: 0,
132
+ used_visits: 7,
133
+ expected_visits_at_date: 3,
134
+ reset_date: '05-11-2022',
135
+ pace: 4,
136
+ pace_indicator: "🐇",
137
+ pace_suggestion: "less than once per week".
138
+ frequency: "6Mx30ST"
139
+ }, {
140
+ discipline: 'Physical Therapy',
141
+ remaining_visits: 0,
142
+ expected_visits_at_date: 3,
143
+ used_visits: 7,
144
+ reset_date: '05-11-2022',
145
+ pace: 4,
146
+ pace_indicator: "🐇",
147
+ pace_suggestion: "less than once per week",
148
+ frequency: "6Mx30PT"
149
+ }
150
+ ]
151
+ =end
152
+
153
+ paced.interval # Return current interval start and end dates
154
+
155
+ # Below is the result you will get
156
+ =begin
157
+ => [
158
+ {
159
+ discipline: 'Speech Therapy',
160
+ start_date: '04-01-2022',
161
+ reset_date: '05-01-2022'
162
+ },
163
+ {
164
+ discipline: 'Physical Therapy',
165
+ start_date: '04-01-2022',
166
+ reset_date: '05-01-2022'
167
+ }
168
+ ]
169
+ # =>
170
+ =end
119
171
  ```
120
172
 
173
+ Interval `start_date` and `end_date`'s are different from start and end dates for the service. Here they define the start and end dates under evaluation in a specific interval, be it `month`, `week`, or `year`. They represent when the bounds of an interval.
174
+
121
175
  It is important to note that the `pace` is hugely influenced by the `summer_holidays` period and the `mode` in which it is calculated.
122
176
 
123
177
  ## Data Types
@@ -0,0 +1,75 @@
1
+ require 'date'
2
+ require 'holidays'
3
+
4
+ module Pacing
5
+ class Error
6
+ attr_reader :school_plan, :date, :non_business_days, :state, :mode, :interval, :summer_holidays
7
+
8
+ def initialize(school_plan:, date:, non_business_days:, state: :us_tn, mode: :liberal, summer_holidays: [])
9
+ @school_plan = school_plan
10
+ @non_business_days = non_business_days
11
+ @date = date
12
+ @state = state
13
+ @mode = [:strict, :liberal].include?(mode) ? mode : :liberal
14
+
15
+ raise ArgumentError.new("You must pass in at least one school plan") if school_plan.nil?
16
+ raise TypeError.new("School plan must be a hash") if school_plan.class != Hash
17
+
18
+ raise ArgumentError.new('You must pass in a date') if date.nil?
19
+ 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)
20
+ raise ArgumentError.new('Date must be within the interval range of the school plan') if !date_within_range
21
+
22
+ non_business_days.each do |non_business_day|
23
+ 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)
24
+ end
25
+
26
+ school_plan[:school_plan_services].each do |school_plan_service|
27
+ 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?
28
+
29
+ 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?
30
+
31
+ 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])
32
+
33
+ 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])
34
+
35
+ 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?
36
+
37
+ raise TypeError.new("Frequency must be an integer and cannot be nil") if school_plan_service[:frequency].class != Integer || school_plan_service[:frequency].nil?
38
+
39
+ raise TypeError.new("Interval must be a string and cannot be nil") if school_plan_service[:interval].class != String || school_plan_service[:interval].nil?
40
+
41
+ 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?
42
+
43
+ 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?
44
+
45
+ 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?
46
+
47
+ 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?
48
+ end
49
+ end
50
+
51
+ def date_within_range
52
+ valid_range_or_exceptions = false
53
+
54
+ begin
55
+ @school_plan[:school_plan_services].each do |school_plan_service|
56
+ if (parse_date(school_plan_service[:start_date]) < parse_date(@date) && parse_date(@date) < parse_date(school_plan_service[:end_date]))
57
+ valid_range_or_exceptions = true
58
+ end
59
+ end
60
+ rescue => exception
61
+ valid_range_or_exceptions = true
62
+ end
63
+
64
+ valid_range_or_exceptions
65
+ end
66
+
67
+ def parse_date(date)
68
+ begin
69
+ Date.strptime(date, '%m-%d-%Y')
70
+ rescue => exception
71
+ end
72
+ end
73
+ end
74
+ end
75
+
@@ -0,0 +1,210 @@
1
+ require 'date'
2
+ require 'holidays'
3
+
4
+ module Pacing
5
+ class Normalizer
6
+
7
+ attr_accessor :services, :date
8
+
9
+ def initialize(services, date)
10
+ @date = date
11
+ @services = active_services(services)
12
+ end
13
+
14
+ def normalize
15
+ { school_plan_services: disciplines_cleaner([speech_discipline, occupational_discipline, physical_discipline, feeding_discipline]) }
16
+ end
17
+
18
+ def speech_discipline
19
+ discipline = {
20
+ :school_plan_type => 'IEP',
21
+ :start_date => "01-01-2100", # some arbitrary start date
22
+ :end_date => "01-01-2000", # some arbitrary end date
23
+ :type_of_service => 'Speech Therapy',
24
+ :frequency => 0,
25
+ :interval => '',
26
+ :time_per_session_in_minutes => 0,
27
+ :completed_visits_for_current_interval => 0,
28
+ :extra_sessions_allowable => 0,
29
+ :interval_for_extra_sessions_allowable => ''
30
+ }
31
+
32
+ discipline_services = services.filter do |service|
33
+ ["pragmatic language", "speech and language", "language", "speech", "language therapy", "speech therapy", "speech and language therapy", "speech language therapy"].include?(service[:type_of_service].downcase)
34
+ end
35
+
36
+ return {} if discipline_services.empty?
37
+
38
+ discipline_services = normalize_to_monthly_frequency(discipline_services)
39
+
40
+ discipline_data(discipline_services, discipline)
41
+ end
42
+
43
+ def occupational_discipline
44
+ discipline = {
45
+ :school_plan_type => 'IEP',
46
+ :start_date => "01-01-2100", # some arbitrary start date
47
+ :end_date => "01-01-2000", # some arbitrary end date
48
+ :type_of_service => 'Occupational Therapy',
49
+ :frequency => 0,
50
+ :interval => '',
51
+ :time_per_session_in_minutes => 0,
52
+ :completed_visits_for_current_interval => 0,
53
+ :extra_sessions_allowable => 0,
54
+ :interval_for_extra_sessions_allowable => ''
55
+ }
56
+
57
+ discipline_services = services.filter do |service|
58
+ ["occupation therapy", "occupational therapy", "occupation"].include?(service[:type_of_service].downcase)
59
+ end
60
+
61
+ return {} if discipline_services.empty?
62
+
63
+ discipline_services = normalize_to_monthly_frequency(discipline_services)
64
+
65
+ discipline_data(discipline_services, discipline)
66
+ end
67
+
68
+ def physical_discipline
69
+ discipline = {
70
+ :school_plan_type => 'IEP',
71
+ :start_date => "01-01-2100", # some arbitrary start date
72
+ :end_date => "01-01-2000", # some arbitrary end date
73
+ :type_of_service => 'Physical Therapy',
74
+ :frequency => 0,
75
+ :interval => '',
76
+ :time_per_session_in_minutes => 0,
77
+ :completed_visits_for_current_interval => 0,
78
+ :extra_sessions_allowable => 0,
79
+ :interval_for_extra_sessions_allowable => ''
80
+ }
81
+
82
+ discipline_services = services.filter do |service|
83
+ ["physical therapy", "physical"].include?(service[:type_of_service].downcase)
84
+ end
85
+
86
+ return {} if discipline_services.empty?
87
+
88
+ discipline_services = normalize_to_monthly_frequency(discipline_services)
89
+
90
+ discipline_data(discipline_services, discipline)
91
+ end
92
+
93
+ def feeding_discipline
94
+ discipline = {
95
+ :school_plan_type => 'IEP',
96
+ :start_date => "01-01-2100", # some arbitrary start date
97
+ :end_date => "01-01-2000", # some arbitrary end date
98
+ :type_of_service => 'Feeding Therapy',
99
+ :frequency => 0,
100
+ :interval => '',
101
+ :time_per_session_in_minutes => 0,
102
+ :completed_visits_for_current_interval => 0,
103
+ :extra_sessions_allowable => 0,
104
+ :interval_for_extra_sessions_allowable => ''
105
+ }
106
+
107
+ discipline_services = services.filter do |service|
108
+ ["feeding therapy", "feeding"].include?(service[:type_of_service].downcase)
109
+ end
110
+
111
+ return {} if discipline_services.empty?
112
+
113
+ discipline_services = normalize_to_monthly_frequency(discipline_services)
114
+
115
+ discipline_data(discipline_services, discipline)
116
+ end
117
+
118
+ def discipline_data(services, discipline)
119
+ services.each do |service|
120
+ discipline[:start_date] = parse_date(service[:start_date]) < parse_date(discipline[:start_date]) ? service[:start_date] : discipline[:start_date]
121
+
122
+ discipline[:end_date] = parse_date(service[:end_date]) > parse_date(discipline[:end_date]) ? service[:end_date] : discipline[:end_date]
123
+
124
+ discipline[:frequency] += service[:frequency].to_i
125
+
126
+ discipline[:completed_visits_for_current_interval] = service[:completed_visits_for_current_interval] if service[:completed_visits_for_current_interval] > discipline[:completed_visits_for_current_interval]
127
+
128
+ discipline[:time_per_session_in_minutes] = service[:time_per_session_in_minutes] > discipline[:time_per_session_in_minutes] ? service[:time_per_session_in_minutes] : discipline[:time_per_session_in_minutes]
129
+
130
+ discipline[:interval] = service[:interval]
131
+
132
+ discipline[:extra_sessions_allowable] += service[:extra_sessions_allowable].to_i
133
+
134
+ discipline[:interval_for_extra_sessions_allowable] = service[:interval_for_extra_sessions_allowable]
135
+ end
136
+
137
+ discipline
138
+ end
139
+
140
+ def same_interval(services)
141
+ interval = services[0].nil? ? "" : services[0][:interval]
142
+ same = true
143
+
144
+ services.each do |service|
145
+ if interval != service[:interval]
146
+ # puts "this happened for real? interval #{interval} and service interval #{service[:interval]} #{services}"
147
+ same = false
148
+ end
149
+ end
150
+
151
+ same
152
+ end
153
+
154
+ def normalize_to_monthly_frequency(services)
155
+ # average business days for each interval
156
+ interval_average_days = {
157
+ "weekly" => 5,
158
+ "monthly" => 22,
159
+ "yearly" => 210 # take away average holidays period with is 2.5 months
160
+ }
161
+
162
+ return services if same_interval(services)
163
+
164
+ services.map do |service|
165
+ if !(service[:interval] == "monthly")
166
+ # weekly(5 days) = frequency # weekly
167
+ # monthly(20 days) = frequency * monthly
168
+ # yearly(200 days)
169
+
170
+ f = service[:frequency]
171
+
172
+ service[:frequency] = ((service[:frequency] * interval_average_days["monthly"].to_f) / interval_average_days[service[:interval]]).round
173
+
174
+ service[:interval] = "monthly"
175
+ end
176
+
177
+ service
178
+ end
179
+
180
+ services
181
+ end
182
+
183
+ def parse_date(date)
184
+ begin
185
+ Date.strptime(date, '%m-%d-%Y')
186
+ rescue => exception
187
+ end
188
+ end
189
+
190
+ def disciplines_cleaner(disciplines)
191
+ # use the fake arbitrary reset date to remove unrequired disciplines
192
+ disciplines.filter { |discipline| !discipline.empty? }
193
+ end
194
+
195
+ def active_services(services)
196
+ services.filter do |school_plan_service|
197
+ within = true
198
+ begin
199
+ if !(parse_date(school_plan_service[:start_date]) <= parse_date(date) && parse_date(date) <= parse_date(school_plan_service[:end_date]))
200
+ within = false
201
+ end
202
+ rescue => exception
203
+ end
204
+
205
+ within
206
+ end
207
+ end
208
+ end
209
+ end
210
+
data/lib/pacing/pacer.rb CHANGED
@@ -2,59 +2,57 @@ require 'date'
2
2
  require 'holidays'
3
3
 
4
4
  module Pacing
5
- # two modes(strict: use start dates strictly in calculating pacing)
6
5
  class Pacer
7
6
  COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
8
- attr_reader :school_plan, :date, :non_business_days, :state, :mode, :interval, :summer_holidays
7
+ attr_reader :school_plan, :date, :non_business_days, :state, :mode, :interval, :summer_holidays, :show_frequency
9
8
 
10
- def initialize(school_plan:, date:, non_business_days:, state: :us_tn, mode: :liberal, summer_holidays: [])
9
+ def initialize(school_plan:, date:, non_business_days:, state: :us_tn, mode: :liberal, summer_holidays: [], show_frequency: false)
11
10
  @school_plan = school_plan
12
11
  @non_business_days = non_business_days
13
12
  @date = date
14
13
  @state = state
14
+ @show_frequency = show_frequency
15
15
  @mode = [:strict, :liberal].include?(mode) ? mode : :liberal
16
16
 
17
- raise ArgumentError.new("You must pass in at least one school plan") if @school_plan.nil?
18
- raise TypeError.new("School plan must be a hash") if @school_plan.class != Hash
17
+ Pacing::Error.new(school_plan: school_plan, date: date, non_business_days: non_business_days, state: state, mode: mode, summer_holidays: summer_holidays)
19
18
 
20
- raise ArgumentError.new('You must pass in a date') if @date.nil?
21
- 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)
22
- raise ArgumentError.new('Date must be within the interval range of the school plan') if !date_within_range
23
-
24
- @non_business_days.each do |non_business_day|
25
- 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)
26
- end
27
-
28
- @school_plan[:school_plan_services].each do |school_plan_service|
29
- 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?
30
-
31
- 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?
32
-
33
- 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])
34
-
35
- 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])
36
-
37
- 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?
38
-
39
- raise TypeError.new("Frequency must be an integer and cannot be nil") if school_plan_service[:frequency].class != Integer || school_plan_service[:frequency].nil?
40
-
41
- raise TypeError.new("Interval must be a string and cannot be nil") if school_plan_service[:interval].class != String || school_plan_service[:interval].nil?
19
+ @summer_holidays = summer_holidays.empty? ? parse_summer_holiday_dates : [parse_date(summer_holidays[0]), parse_date(summer_holidays[1])]
20
+ end
42
21
 
43
- 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?
22
+ def interval
23
+ # filter out services that haven't started or whose time is passed
24
+ services = @school_plan[:school_plan_services].filter do |school_plan_service|
25
+ within = true
26
+ if !(parse_date(school_plan_service[:start_date]) <= parse_date(@date) && parse_date(@date) <= parse_date(school_plan_service[:end_date]))
27
+ within = false
28
+ end
44
29
 
45
- 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?
30
+ within
31
+ end
46
32
 
47
- 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?
33
+ services = services.map do |service|
34
+ 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)
35
+ discipline_name = "Speech Therapy"
36
+ elsif ["occupation therapy", "occupational therapy"].include?(service[:type_of_service].downcase)
37
+ discipline_name = "Occupational Therapy"
38
+ elsif ["physical therapy"].include?(service[:type_of_service].downcase)
39
+ discipline_name = "Physical Therapy"
40
+ elsif ["feeding therapy"].include?(service[:type_of_service].downcase)
41
+ discipline_name = "Feeding Therapy"
42
+ end
48
43
 
49
- 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?
44
+ discipline = {}
45
+ discipline[:discipline] = discipline_name
46
+ discipline[:reset_date] = reset_date(start_date: service[:start_date], interval: service[:interval])
47
+ discipline[:start_date] = start_of_treatment_date(parse_date(service[:start_date]), service[:interval]).strftime("%m-%d-%Y")
48
+ discipline
50
49
  end
51
-
52
- @summer_holidays = summer_holidays.empty? ? parse_summer_holiday_dates : [parse_date(summer_holidays[0]), parse_date(summer_holidays[1])]
53
50
  end
54
51
 
55
52
  def calculate
56
53
  # filter out services that haven't started or whose time is passed
57
- services = @school_plan[:school_plan_services].filter do |school_plan_service|
54
+ school_plan_services = Pacing::Normalizer.new(@school_plan[:school_plan_services], @date).normalize
55
+ services = school_plan_services[:school_plan_services].filter do |school_plan_service|
58
56
  within = true
59
57
  if !(parse_date(school_plan_service[:start_date]) <= parse_date(@date) && parse_date(@date) <= parse_date(school_plan_service[:end_date]))
60
58
  within = false
@@ -84,12 +82,26 @@ module Pacing
84
82
 
85
83
  discipline[:expected_visits_at_date] = expected
86
84
 
87
- discipline[:type_of_service] = service[:type_of_service]
85
+ discipline[:discipline] = service[:type_of_service]
86
+
87
+ discipline[:pace_indicator] = pace_indicator(discipline[:pace])
88
+ discipline[:pace_suggestion] = readable_suggestion(rate: discipline[:suggested_rate])
89
+
90
+ if show_frequency
91
+ discipline[:frequency] = discipline_frequency(service)
92
+ end
93
+
94
+ discipline.delete(:suggested_rate)
88
95
 
89
96
  discipline
90
97
  end
91
98
 
92
- disciplines_cleaner ([speech_discipline(services), occupational_discipline(services), physical_discipline(services), feeding_discipline(services)])
99
+ services
100
+ end
101
+
102
+ # discipline iep frequency
103
+ def discipline_frequency(service)
104
+ service[:frequency].to_s + service[:interval][0].gsub(/(per)|p/i, "").strip.upcase + "x" + service[:time_per_session_in_minutes].to_s + service[:type_of_service][0].upcase + "T"
93
105
  end
94
106
 
95
107
  # get a spreadout of visit dates over an interval by using simple proportion.
@@ -215,6 +227,7 @@ module Pacing
215
227
 
216
228
  # get actual date of the first day of the week where date falls
217
229
  def week_start(date, offset_from_sunday=0)
230
+ offset_from_sunday = @mode == :liberal ? 1 : 0
218
231
  return date if date.monday?
219
232
  date - ((date.wday - offset_from_sunday) % 7)
220
233
  end
@@ -226,7 +239,9 @@ module Pacing
226
239
 
227
240
  # reset date for the monthly interval
228
241
  def reset_date_monthly(start_date, interval)
229
- (start_of_treatment_date(parse_date(start_date), interval) + COMMON_YEAR_DAYS_IN_MONTH[(parse_date(@date)).month]).strftime("%m-%d-%Y")
242
+ month = (parse_date(@date)).month
243
+
244
+ (start_of_treatment_date(parse_date(start_date), interval) + COMMON_YEAR_DAYS_IN_MONTH[month]).strftime("%m-%d-%Y")
230
245
  end
231
246
 
232
247
  # reset date for the weekly interval
@@ -245,7 +260,7 @@ module Pacing
245
260
  # start_date
246
261
  end
247
262
 
248
- # start of treatment for the montly interval
263
+ # start of treatment for the monthly interval
249
264
  def start_of_treatment_date_monthly(start_date)
250
265
  if @mode == :strict
251
266
  return parse_date("#{parse_date(@date).month}-#{start_date.day}-#{parse_date(@date).year}")
@@ -260,12 +275,14 @@ module Pacing
260
275
 
261
276
  # start of treatment for the weekly interval
262
277
  def start_of_treatment_date_weekly(start_date)
278
+ # TODO: Update with assumption that Monday is start of week
279
+ # Future TODO: allow user to pass in configuration for start of week
263
280
  parsed_date = parse_date(@date)
264
281
  week_start_date = week_start(parsed_date)
265
282
  weekly_date = week_start_date
266
283
 
267
284
  if week_start_date != parsed_date && @mode == :strict
268
- weekly_date = week_start_date + start_date.wday #unless start_date.wday == 1
285
+ weekly_date = week_start_date + start_date.wday # unless start_date.wday == 1
269
286
  weekly_date = parsed_date < weekly_date ? weekly_date - 7 : weekly_date
270
287
  end
271
288
 
@@ -288,112 +305,6 @@ module Pacing
288
305
  valid_range_or_exceptions
289
306
  end
290
307
 
291
- def speech_discipline(services)
292
- discipline = {
293
- :discipline => "Speech Therapy",
294
- :remaining_visits => 0,
295
- :used_visits => 0,
296
- :pace => 0,
297
- :pace_indicator => "🐢",
298
- :pace_suggestion => "once a day",
299
- :suggested_rate => 0,
300
- :expected_visits_at_date => 0,
301
- :reset_date => nil } # some arbitrarity date in the past
302
-
303
- discipline_services = services.filter do |service|
304
- ["pragmatic language", "speech and language", "language", "speech", "language therapy", "speech therapy", "speech and language therapy", "speech language therapy"].include?(service[:type_of_service].downcase)
305
- end
306
-
307
- return {} if discipline_services.empty?
308
-
309
- discipline_data(discipline_services, discipline)
310
- end
311
-
312
- def occupational_discipline(services)
313
- discipline = {
314
- :discipline=>"Occupational Therapy",
315
- :remaining_visits=>0,
316
- :used_visits=>0,
317
- :pace=>0,
318
- :pace_indicator=>"🐢",
319
- :pace_suggestion=>"once a day",
320
- :suggested_rate => 0,
321
- :expected_visits_at_date=>0,
322
- :reset_date=> nil } # some arbitrarity date in the past
323
-
324
- discipline_services = services.filter do |service|
325
- ["occupation therapy", "occupational therapy"].include?(service[:type_of_service].downcase)
326
- end
327
-
328
- return {} if discipline_services.empty?
329
-
330
- discipline_data(discipline_services, discipline)
331
- end
332
-
333
- def physical_discipline(services)
334
- discipline = {
335
- :discipline=>"Physical Therapy",
336
- :remaining_visits=>0,
337
- :used_visits=>0,
338
- :pace=>0,
339
- :pace_indicator=>"🐢",
340
- :pace_suggestion=>"once a day",
341
- :suggested_rate => 0,
342
- :expected_visits_at_date=>0,
343
- :reset_date=> nil } # some arbitrarity date in the past
344
-
345
- discipline_services = services.filter do |service|
346
- ["physical therapy"].include?(service[:type_of_service].downcase)
347
- end
348
-
349
- return {} if discipline_services.empty?
350
-
351
- discipline_data(discipline_services, discipline)
352
- end
353
-
354
- def feeding_discipline(services)
355
- discipline = {
356
- :discipline=>"Feeding Therapy",
357
- :remaining_visits=>0,
358
- :used_visits=>0,
359
- :pace=>0,
360
- :pace_indicator=>"🐢",
361
- :pace_suggestion=>"once a day",
362
- :suggested_rate => 0,
363
- :expected_visits_at_date=>0,
364
- :reset_date=> nil } # some arbitrarity date in the past
365
-
366
- discipline_services = services.filter do |service|
367
- ["Feeding Therapy"].include? service[:type_of_service]
368
- end
369
-
370
- return {} if discipline_services.empty?
371
-
372
- discipline_data(discipline_services, discipline)
373
- end
374
-
375
- def discipline_data(services, discipline)
376
- services.each do |service|
377
- discipline[:pace] = discipline[:pace] ? discipline[:pace].to_i + service[:pace].to_i : service[:pace]
378
-
379
- discipline[:remaining_visits] = discipline[:remaining_visits] ? discipline[:remaining_visits].to_i + service[:remaining_visits].to_i : service[:remaining_visits]
380
-
381
- discipline[:used_visits] = discipline[:used_visits] ? discipline[:used_visits].to_i + service[:used_visits].to_i : service[:used_visits]
382
-
383
- 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]
384
-
385
- discipline[:suggested_rate] = discipline[:suggested_rate] ? discipline[:suggested_rate].to_f + service[:suggested_rate].to_f : service[:suggested_rate].to_f
386
-
387
- discipline[:reset_date] = (!discipline[:reset_date].nil? && parse_date(service[:reset_date]) < parse_date(discipline[:reset_date])) ? discipline[:reset_date] : service[:reset_date]
388
- end
389
-
390
- discipline[:pace_indicator] = pace_indicator(discipline[:pace])
391
- discipline[:pace_suggestion] = readable_suggestion(rate: discipline[:suggested_rate])
392
-
393
- discipline.delete(:suggested_rate)
394
- discipline
395
- end
396
-
397
308
  def readable_suggestion(rate:)
398
309
  # rate = suggested_rate(remaining_visits: remaining_visits, start_date: start_date, interval: interval)
399
310
 
@@ -429,11 +340,6 @@ module Pacing
429
340
  days_left
430
341
  end
431
342
 
432
- def disciplines_cleaner(disciplines)
433
- # use the fake arbitrary reset date to remove unrequired disciplines
434
- disciplines.filter { |discipline| !discipline.empty? }
435
- end
436
-
437
343
  def parse_summer_holiday_dates
438
344
  holidays_start = parse_date("05-13-#{parse_date(@date).year}")
439
345
  holidays_start += 1 until holidays_start.wday == 5
@@ -442,7 +348,6 @@ module Pacing
442
348
  holidays_start += 1 until holidays_start.wday == 1
443
349
 
444
350
  [holidays_start, holidays_end]
445
- end
351
+ end
446
352
  end
447
353
  end
448
-
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pacing
4
- VERSION = "1.0.1"
4
+ VERSION = "2.1.0"
5
5
  end
data/lib/pacing.rb CHANGED
@@ -1,2 +1,4 @@
1
+ require "pacing/normalizer"
2
+ require "pacing/error"
1
3
  require "pacing/pacer"
2
4
  require "pacing/version"