pacing 1.0.1 → 2.0.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 +21 -0
- data/lib/pacing/error.rb +75 -0
- data/lib/pacing/normalizer.rb +210 -0
- data/lib/pacing/pacer.rb +45 -150
- data/lib/pacing/version.rb +1 -1
- data/lib/pacing.rb +2 -0
- data/spec/pacing_spec.rb +326 -134
- 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: a71bff11bdcc21f74452c5217d348302f8e3680e3c2ed9d753ba1acd156f4d4a
|
4
|
+
data.tar.gz: b78da81e0108e399aba1b422f56ce5509948d2708e8b6c5a011a0f82d91086fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87c3abd50e088736eae74610469c86972c2ec09c0e8da94d3c7e27dd02e34d62aef25ddab2acf602d90d46e8aefd515d3b1d6102fac37dd813bcbd24ef71f507
|
7
|
+
data.tar.gz: 98a2d9a5b3fc8bd62630273ead673bce8a2c2ec3667dd706b3e3ab8efdbdd6db95df39501d8ac0a08170db4b40d6c66439fad08f6937a8c1b61bc97051377c7e
|
data/README.md
CHANGED
@@ -116,8 +116,29 @@ paced.calculate
|
|
116
116
|
}
|
117
117
|
]
|
118
118
|
=end
|
119
|
+
|
120
|
+
paced.interval # Return current interval start and end dates
|
121
|
+
|
122
|
+
# Below is the result you will get
|
123
|
+
=begin
|
124
|
+
=> [
|
125
|
+
{
|
126
|
+
discipline: 'Speech Therapy',
|
127
|
+
start_date: '04-01-2022',
|
128
|
+
reset_date: '05-01-2022'
|
129
|
+
},
|
130
|
+
{
|
131
|
+
discipline: 'Physical Therapy',
|
132
|
+
start_date: '04-01-2022',
|
133
|
+
reset_date: '05-01-2022'
|
134
|
+
}
|
135
|
+
]
|
136
|
+
# =>
|
137
|
+
=end
|
119
138
|
```
|
120
139
|
|
140
|
+
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.
|
141
|
+
|
121
142
|
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
143
|
|
123
144
|
## 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,7 +2,6 @@ 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
7
|
attr_reader :school_plan, :date, :non_business_days, :state, :mode, :interval, :summer_holidays
|
@@ -14,47 +13,45 @@ module Pacing
|
|
14
13
|
@state = state
|
15
14
|
@mode = [:strict, :liberal].include?(mode) ? mode : :liberal
|
16
15
|
|
17
|
-
|
18
|
-
raise TypeError.new("School plan must be a hash") if @school_plan.class != Hash
|
16
|
+
Pacing::Error.new(school_plan: school_plan, date: date, non_business_days: non_business_days, state: state, mode: mode, summer_holidays: summer_holidays)
|
19
17
|
|
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?
|
18
|
+
@summer_holidays = summer_holidays.empty? ? parse_summer_holiday_dates : [parse_date(summer_holidays[0]), parse_date(summer_holidays[1])]
|
19
|
+
end
|
42
20
|
|
43
|
-
|
21
|
+
def interval
|
22
|
+
# filter out services that haven't started or whose time is passed
|
23
|
+
services = @school_plan[:school_plan_services].filter do |school_plan_service|
|
24
|
+
within = true
|
25
|
+
if !(parse_date(school_plan_service[:start_date]) <= parse_date(@date) && parse_date(@date) <= parse_date(school_plan_service[:end_date]))
|
26
|
+
within = false
|
27
|
+
end
|
44
28
|
|
45
|
-
|
29
|
+
within
|
30
|
+
end
|
46
31
|
|
47
|
-
|
32
|
+
services = services.map do |service|
|
33
|
+
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)
|
34
|
+
discipline_name = "Speech Therapy"
|
35
|
+
elsif ["occupation therapy", "occupational therapy"].include?(service[:type_of_service].downcase)
|
36
|
+
discipline_name = "Occupational Therapy"
|
37
|
+
elsif ["physical therapy"].include?(service[:type_of_service].downcase)
|
38
|
+
discipline_name = "Physical Therapy"
|
39
|
+
elsif ["feeding therapy"].include?(service[:type_of_service].downcase)
|
40
|
+
discipline_name = "Feeding Therapy"
|
41
|
+
end
|
48
42
|
|
49
|
-
|
43
|
+
discipline = {}
|
44
|
+
discipline[:discipline] = discipline_name
|
45
|
+
discipline[:reset_date] = reset_date(start_date: service[:start_date], interval: service[:interval])
|
46
|
+
discipline[:start_date] = start_of_treatment_date(parse_date(service[:start_date]), service[:interval]).strftime("%m-%d-%Y")
|
47
|
+
discipline
|
50
48
|
end
|
51
|
-
|
52
|
-
@summer_holidays = summer_holidays.empty? ? parse_summer_holiday_dates : [parse_date(summer_holidays[0]), parse_date(summer_holidays[1])]
|
53
49
|
end
|
54
50
|
|
55
51
|
def calculate
|
56
52
|
# filter out services that haven't started or whose time is passed
|
57
|
-
|
53
|
+
school_plan_services = Pacing::Normalizer.new(@school_plan[:school_plan_services], @date).normalize
|
54
|
+
services = school_plan_services[:school_plan_services].filter do |school_plan_service|
|
58
55
|
within = true
|
59
56
|
if !(parse_date(school_plan_service[:start_date]) <= parse_date(@date) && parse_date(@date) <= parse_date(school_plan_service[:end_date]))
|
60
57
|
within = false
|
@@ -84,12 +81,17 @@ module Pacing
|
|
84
81
|
|
85
82
|
discipline[:expected_visits_at_date] = expected
|
86
83
|
|
87
|
-
discipline[:
|
84
|
+
discipline[:discipline] = service[:type_of_service]
|
85
|
+
|
86
|
+
discipline[:pace_indicator] = pace_indicator(discipline[:pace])
|
87
|
+
discipline[:pace_suggestion] = readable_suggestion(rate: discipline[:suggested_rate])
|
88
|
+
|
89
|
+
discipline.delete(:suggested_rate)
|
88
90
|
|
89
91
|
discipline
|
90
92
|
end
|
91
93
|
|
92
|
-
|
94
|
+
services
|
93
95
|
end
|
94
96
|
|
95
97
|
# get a spreadout of visit dates over an interval by using simple proportion.
|
@@ -215,6 +217,7 @@ module Pacing
|
|
215
217
|
|
216
218
|
# get actual date of the first day of the week where date falls
|
217
219
|
def week_start(date, offset_from_sunday=0)
|
220
|
+
offset_from_sunday = @mode == :liberal ? 1 : 0
|
218
221
|
return date if date.monday?
|
219
222
|
date - ((date.wday - offset_from_sunday) % 7)
|
220
223
|
end
|
@@ -226,7 +229,9 @@ module Pacing
|
|
226
229
|
|
227
230
|
# reset date for the monthly interval
|
228
231
|
def reset_date_monthly(start_date, interval)
|
229
|
-
|
232
|
+
month = (parse_date(@date)).month
|
233
|
+
|
234
|
+
(start_of_treatment_date(parse_date(start_date), interval) + COMMON_YEAR_DAYS_IN_MONTH[month]).strftime("%m-%d-%Y")
|
230
235
|
end
|
231
236
|
|
232
237
|
# reset date for the weekly interval
|
@@ -245,7 +250,7 @@ module Pacing
|
|
245
250
|
# start_date
|
246
251
|
end
|
247
252
|
|
248
|
-
# start of treatment for the
|
253
|
+
# start of treatment for the monthly interval
|
249
254
|
def start_of_treatment_date_monthly(start_date)
|
250
255
|
if @mode == :strict
|
251
256
|
return parse_date("#{parse_date(@date).month}-#{start_date.day}-#{parse_date(@date).year}")
|
@@ -260,12 +265,14 @@ module Pacing
|
|
260
265
|
|
261
266
|
# start of treatment for the weekly interval
|
262
267
|
def start_of_treatment_date_weekly(start_date)
|
268
|
+
# TODO: Update with assumption that Monday is start of week
|
269
|
+
# Future TODO: allow user to pass in configuration for start of week
|
263
270
|
parsed_date = parse_date(@date)
|
264
271
|
week_start_date = week_start(parsed_date)
|
265
272
|
weekly_date = week_start_date
|
266
273
|
|
267
274
|
if week_start_date != parsed_date && @mode == :strict
|
268
|
-
weekly_date = week_start_date + start_date.wday #unless start_date.wday == 1
|
275
|
+
weekly_date = week_start_date + start_date.wday # unless start_date.wday == 1
|
269
276
|
weekly_date = parsed_date < weekly_date ? weekly_date - 7 : weekly_date
|
270
277
|
end
|
271
278
|
|
@@ -288,112 +295,6 @@ module Pacing
|
|
288
295
|
valid_range_or_exceptions
|
289
296
|
end
|
290
297
|
|
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
298
|
def readable_suggestion(rate:)
|
398
299
|
# rate = suggested_rate(remaining_visits: remaining_visits, start_date: start_date, interval: interval)
|
399
300
|
|
@@ -429,11 +330,6 @@ module Pacing
|
|
429
330
|
days_left
|
430
331
|
end
|
431
332
|
|
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
333
|
def parse_summer_holiday_dates
|
438
334
|
holidays_start = parse_date("05-13-#{parse_date(@date).year}")
|
439
335
|
holidays_start += 1 until holidays_start.wday == 5
|
@@ -442,7 +338,6 @@ module Pacing
|
|
442
338
|
holidays_start += 1 until holidays_start.wday == 1
|
443
339
|
|
444
340
|
[holidays_start, holidays_end]
|
445
|
-
end
|
341
|
+
end
|
446
342
|
end
|
447
343
|
end
|
448
|
-
|
data/lib/pacing/version.rb
CHANGED
data/lib/pacing.rb
CHANGED