pacing 1.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +54 -0
- data/lib/pacing/error.rb +75 -0
- data/lib/pacing/normalizer.rb +210 -0
- data/lib/pacing/pacer.rb +57 -152
- data/lib/pacing/version.rb +1 -1
- data/lib/pacing.rb +2 -0
- data/spec/pacing_spec.rb +377 -135
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbc2189e33671c8a34eccd7aa71ec4f93d48fe0228e4931655bda84c519cc1d8
|
4
|
+
data.tar.gz: a1808e5be13be267c73b2720fabafbfe9e379029a209a86f5b19921c515497f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/pacing/error.rb
ADDED
@@ -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
|
-
|
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
|
-
|
21
|
-
|
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
|
-
|
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
|
-
|
30
|
+
within
|
31
|
+
end
|
46
32
|
|
47
|
-
|
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
|
-
|
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
|
-
|
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[:
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
data/lib/pacing/version.rb
CHANGED
data/lib/pacing.rb
CHANGED