pacing 1.0.1 → 2.0.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 +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