gthc 0.0.1 → 0.1.1
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/.gitignore +2 -0
- data/Gemfile.lock +35 -0
- data/README.md +2 -6
- data/lib/gthc.rb +1 -0
- data/lib/gthc/olson.rb +12 -0
- data/lib/gthc/olson/algorithm.rb +148 -0
- data/lib/gthc/olson/helpers.rb +26 -0
- data/lib/gthc/olson/weight.rb +235 -0
- data/lib/gthc/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4a4d5c17afbded52b98ab45a4790c0b40603c84bd4dfdda8b372a098d1350fa
|
4
|
+
data.tar.gz: d08051fe205d84b2619a56a42b3be1ca34e33dc7200d4d61e1e3d0c8c631999b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 70b7aebf6fda10145d3772ff72bd7aa11be6d2e97f4a59affb1ed1f1da43f161efcbd4fa835c5b4199361a8d4908f69061056fdedf869f7ba4602da58c744c85
|
7
|
+
data.tar.gz: f160ec0134082c62c56929558a6e2cd59c60d18106a389320bd4b422e43615aaa0c163293e7097f12e77e4d09392a53e74938f561d8296ce47c7d772aff46d3d
|
data/.gitignore
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
gthc (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.3)
|
10
|
+
rake (10.5.0)
|
11
|
+
rspec (3.8.0)
|
12
|
+
rspec-core (~> 3.8.0)
|
13
|
+
rspec-expectations (~> 3.8.0)
|
14
|
+
rspec-mocks (~> 3.8.0)
|
15
|
+
rspec-core (3.8.2)
|
16
|
+
rspec-support (~> 3.8.0)
|
17
|
+
rspec-expectations (3.8.4)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.8.0)
|
20
|
+
rspec-mocks (3.8.1)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.8.0)
|
23
|
+
rspec-support (3.8.2)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
bundler (~> 2.0)
|
30
|
+
gthc!
|
31
|
+
rake (~> 10.0)
|
32
|
+
rspec (~> 3.0)
|
33
|
+
|
34
|
+
BUNDLED WITH
|
35
|
+
2.0.2
|
data/README.md
CHANGED
@@ -1,8 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/gthc`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
1
|
+
# GTHC
|
6
2
|
|
7
3
|
## Installation
|
8
4
|
|
@@ -40,4 +36,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
40
36
|
|
41
37
|
## Code of Conduct
|
42
38
|
|
43
|
-
Everyone interacting in the
|
39
|
+
Everyone interacting in the GTHC project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/gthc/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/gthc.rb
CHANGED
data/lib/gthc/olson.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require "gthc/olson/helpers"
|
2
|
+
require "gthc/olson/weight"
|
3
|
+
|
4
|
+
module Algorithm
|
5
|
+
|
6
|
+
include Helpers
|
7
|
+
extend Weight
|
8
|
+
|
9
|
+
# Central document for creating schedule.
|
10
|
+
def schedule(people, scheduleGrid)
|
11
|
+
scheduleLength = scheduleGrid[0].length
|
12
|
+
|
13
|
+
# Remove all availability slots that are already filled in the schedule.
|
14
|
+
slots, graveyard, people = removeFilledSlots(people, scheduleGrid)
|
15
|
+
|
16
|
+
# Remove all availability slots that are already filled in the schedule.
|
17
|
+
while slots.length > 0
|
18
|
+
|
19
|
+
# Weight Reset - set all weights to 1.
|
20
|
+
slots = Weight.weightReset(slots)
|
21
|
+
|
22
|
+
# Weight Balance - prioritize people with fewer scheduled shifts.
|
23
|
+
people, slots = Weight.weightBalance(people, slots)
|
24
|
+
|
25
|
+
# Weight Contiguous - prioritize people to stay in the tent more time at once.
|
26
|
+
slots, scheduleGrid, graveyard = Weight.weightContiguous(slots, scheduleGrid, graveyard)
|
27
|
+
|
28
|
+
# Weight Tough Time - prioritize time slots with few people available.
|
29
|
+
slots = Weight.weightToughTime(slots, scheduleLength)
|
30
|
+
|
31
|
+
# Sort by Weights
|
32
|
+
slots.sort_by { |a| -a.weight }
|
33
|
+
|
34
|
+
# Update people, spreadsheet, and remove slots.
|
35
|
+
people, slots, graveyard, scheduleGrid = Weight.weightPick(people, slots, graveyard, scheduleGrid)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
return processData(people, scheduleGrid)
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# Remove all availability slots that are already filled in the schedule.
|
45
|
+
def removeFilledSlots(people, scheduleGrid)
|
46
|
+
|
47
|
+
# Reset Slots Array.
|
48
|
+
slots = Array.new
|
49
|
+
# Set up graveyard (Rows that are completely scheduled will go here).
|
50
|
+
graveyard = Array.new(scheduleGrid[0].length, 0)
|
51
|
+
# Set up counterArray (Going to count how scheduled a row is).
|
52
|
+
counterArray = Array.new(scheduleGrid[0].length, 0)
|
53
|
+
|
54
|
+
# Count number of scheduled tenters during a specific time.
|
55
|
+
scheduleGrid.each do | currentPerson |
|
56
|
+
counter = 0
|
57
|
+
while counter < currentPerson.length
|
58
|
+
if currentPerson[counter].status == "Scheduled"
|
59
|
+
counterArray[counter] = counterArray[counter] + 1
|
60
|
+
end
|
61
|
+
counter = counter + 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Iterate through every slot.
|
66
|
+
i = 0
|
67
|
+
while i < scheduleGrid.length
|
68
|
+
|
69
|
+
currentPerson = scheduleGrid[i]
|
70
|
+
counter = 0
|
71
|
+
|
72
|
+
while counter < scheduleGrid[i].length
|
73
|
+
|
74
|
+
# Determine how many people are needed.
|
75
|
+
isNight = currentPerson[counter].isNight
|
76
|
+
phase = currentPerson[counter].phase
|
77
|
+
peopleNeeded = Helpers.calculatePeopleNeeded(isNight, phase)
|
78
|
+
numPeople = counterArray[counter]
|
79
|
+
|
80
|
+
# Only add in slot if necessary.
|
81
|
+
if numPeople < peopleNeeded && currentPerson[counter].status == "Available"
|
82
|
+
slots.push(currentPerson[counter])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Update graveyard
|
86
|
+
if numPeople >= peopleNeeded
|
87
|
+
graveyard[counter] = 1
|
88
|
+
end
|
89
|
+
|
90
|
+
# Update person freedom
|
91
|
+
# if numPeople >= peopleNeeded && currentPerson[counter].status == "Available"
|
92
|
+
# if isNight
|
93
|
+
# people[i].nightFree -= 1
|
94
|
+
# else
|
95
|
+
# people[i].dayFree -= 1
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
|
99
|
+
counter = counter + 1
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
i = i + 1
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
return slots, graveyard, people
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
def processData(people, scheduleGrid)
|
112
|
+
# compress data from 2d grid with a single-deminsion of
|
113
|
+
# all of the scheduled slots
|
114
|
+
combinedGrid = []
|
115
|
+
|
116
|
+
# iterating through every unique slot, and
|
117
|
+
# checking for any people that are scheduled on that slot as well
|
118
|
+
for slotIndex in 0...scheduleGrid[0].length
|
119
|
+
slotData = scheduleGrid[0][slotIndex].to_hash
|
120
|
+
slot = {
|
121
|
+
"startDate": slotData["startDate"],
|
122
|
+
"endDate": slotData["endDate"],
|
123
|
+
"isNight": slotData["isNight"],
|
124
|
+
"phase": slotData["phase"],
|
125
|
+
}
|
126
|
+
slot[:ids] = Array.new
|
127
|
+
|
128
|
+
# checking every person at that slot for status
|
129
|
+
for personIndex in 0...people.length
|
130
|
+
person = people[personIndex]
|
131
|
+
if scheduleGrid[personIndex][slotIndex].status == "Scheduled"
|
132
|
+
slot[:ids].push(person.id)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
combinedGrid.push(slot)
|
137
|
+
end
|
138
|
+
|
139
|
+
# prints amount of people needed in a slot based on time and phase
|
140
|
+
combinedGrid.each do | slot |
|
141
|
+
peopleNeeded = Helpers.calculatePeopleNeeded slot[:isNight], slot[:phase]
|
142
|
+
peopleLeft = peopleNeeded - slot[:ids].length
|
143
|
+
slot["peopleLeft"] = peopleLeft
|
144
|
+
end
|
145
|
+
|
146
|
+
combinedGrid
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Helpers
|
2
|
+
def self.calculatePeopleNeeded(nightBoolean, phase)
|
3
|
+
if phase == "Black"
|
4
|
+
if nightBoolean
|
5
|
+
return 10
|
6
|
+
else
|
7
|
+
return 2
|
8
|
+
end
|
9
|
+
end
|
10
|
+
if phase == "Blue"
|
11
|
+
if nightBoolean
|
12
|
+
return 6
|
13
|
+
else
|
14
|
+
return 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
if phase == "White"
|
18
|
+
if nightBoolean
|
19
|
+
return 2
|
20
|
+
else
|
21
|
+
return 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require "gthc/olson/helpers"
|
2
|
+
|
3
|
+
module Weight
|
4
|
+
include Helpers
|
5
|
+
|
6
|
+
# Weight Reset - set all weights to 1.
|
7
|
+
def self.weightReset(slots)
|
8
|
+
slots.each do | currentSlot |
|
9
|
+
currentSlot.weight = 1;
|
10
|
+
end
|
11
|
+
slots
|
12
|
+
end
|
13
|
+
|
14
|
+
# Weight Balance - prioritize people with fewer scheduled shifts
|
15
|
+
def self.weightBalance(people, slots)
|
16
|
+
|
17
|
+
slots.each do | currentSlot |
|
18
|
+
|
19
|
+
# Establish variables.
|
20
|
+
currentPersonID = currentSlot.personID
|
21
|
+
dayScheduled = people[currentPersonID].dayScheduled
|
22
|
+
nightScheduled = people[currentPersonID].nightScheduled
|
23
|
+
night = currentSlot.isNight;
|
24
|
+
|
25
|
+
nightMulti = 0;
|
26
|
+
dayMulti = 0;
|
27
|
+
|
28
|
+
# Set multipliers.
|
29
|
+
if nightScheduled != 0
|
30
|
+
nightMulti = 1.0 / nightScheduled
|
31
|
+
else
|
32
|
+
nightMulti = 1.5
|
33
|
+
end
|
34
|
+
|
35
|
+
if dayScheduled != 0
|
36
|
+
dayMulti = (1.0/(dayScheduled+nightScheduled*4*2))
|
37
|
+
else
|
38
|
+
dayMulti = 1.5
|
39
|
+
end
|
40
|
+
|
41
|
+
#Adjust weights with multipliers.
|
42
|
+
if night
|
43
|
+
currentSlot.weight = currentSlot.weight * nightMulti
|
44
|
+
else
|
45
|
+
currentSlot.weight = currentSlot.weight * dayMulti
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
return people, slots
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.gridLimits(row, rowLength)
|
53
|
+
return row - 1 < 0,
|
54
|
+
row + 1 > rowLength - 1
|
55
|
+
end
|
56
|
+
|
57
|
+
# Weight Contiguous - prioritize people to stay in the tent more time at once.
|
58
|
+
def self.weightContiguous(slots, scheduleGrid, graveyard)
|
59
|
+
|
60
|
+
i = 0
|
61
|
+
while i < slots.length
|
62
|
+
# Establish Variables
|
63
|
+
currentRow = slots[i].row
|
64
|
+
currentCol = slots[i].col
|
65
|
+
|
66
|
+
aboveRow = currentRow-1
|
67
|
+
belowRow = currentRow+1
|
68
|
+
|
69
|
+
# grab all slots under the same col as the inspected slot in order
|
70
|
+
# to get the slots above and below
|
71
|
+
allSlots = scheduleGrid[currentCol]
|
72
|
+
slotsLength = allSlots.length
|
73
|
+
|
74
|
+
# find what to skip
|
75
|
+
skipAboveRow, skipBelowRow = gridLimits(currentRow, slotsLength)
|
76
|
+
|
77
|
+
currentIsNight = slots[i].isNight
|
78
|
+
aboveIsNight = !skipAboveRow && allSlots[aboveRow].isNight
|
79
|
+
belowIsNight = !skipBelowRow && allSlots[belowRow].isNight
|
80
|
+
|
81
|
+
aboveTent = !skipAboveRow && allSlots[aboveRow].status == "Scheduled"
|
82
|
+
belowTent = !skipBelowRow && allSlots[belowRow].status == "Scheduled"
|
83
|
+
aboveSome = !skipAboveRow && allSlots[aboveRow].status == "Somewhat"
|
84
|
+
belowSome = !skipBelowRow && allSlots[belowRow].status == "Somewhat"
|
85
|
+
aboveFree = !skipAboveRow && allSlots[aboveRow].status == "Available"
|
86
|
+
belowFree = !skipBelowRow && allSlots[belowRow].status == "Available"
|
87
|
+
|
88
|
+
multi = 1
|
89
|
+
|
90
|
+
# Both are scheduled.
|
91
|
+
if aboveTent && belowTent
|
92
|
+
multi = 100
|
93
|
+
end
|
94
|
+
|
95
|
+
# Both are not free
|
96
|
+
if !belowTent && !belowFree && !aboveSome && !belowSome && !aboveTent && !aboveFree
|
97
|
+
if slots[i].weight > 0
|
98
|
+
multi = -1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Above is scheduled, below is free.
|
103
|
+
if aboveTent && !belowTent && belowFree
|
104
|
+
multi = 3.25
|
105
|
+
end
|
106
|
+
|
107
|
+
# Below is scheduled, above is free.
|
108
|
+
if belowTent && !aboveTent && aboveFree
|
109
|
+
multi = 3.25
|
110
|
+
end
|
111
|
+
|
112
|
+
# Above is scheduled, below is not free.
|
113
|
+
if aboveTent && !belowTent && !belowFree
|
114
|
+
multi = 3
|
115
|
+
end
|
116
|
+
|
117
|
+
# Below is scheduled, above is not free.
|
118
|
+
if belowTent && !aboveTent && !aboveFree
|
119
|
+
multi = 3
|
120
|
+
end
|
121
|
+
|
122
|
+
# Both are free
|
123
|
+
if belowFree && aboveFree
|
124
|
+
multi = 2.75
|
125
|
+
end
|
126
|
+
|
127
|
+
# Above is free, below is not free
|
128
|
+
if aboveFree && !belowTent && !belowFree
|
129
|
+
multi = 1
|
130
|
+
end
|
131
|
+
|
132
|
+
# Below is free, above is not free
|
133
|
+
if(belowFree && !aboveTent && !aboveFree)
|
134
|
+
multi = 1
|
135
|
+
end
|
136
|
+
|
137
|
+
# Night Multi
|
138
|
+
if aboveIsNight || belowIsNight || currentIsNight
|
139
|
+
multi *= 1.25
|
140
|
+
end
|
141
|
+
|
142
|
+
# Occurance of Somewhat Available
|
143
|
+
if aboveSome || belowSome
|
144
|
+
multi *= 0.5
|
145
|
+
end
|
146
|
+
|
147
|
+
slots[i].weight = slots[i].weight*multi
|
148
|
+
i += 1
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
return slots, scheduleGrid, graveyard
|
153
|
+
end
|
154
|
+
|
155
|
+
# Weight Tough Time - prioritize time slots with few people available. */
|
156
|
+
def self.weightToughTime(slots, scheduleLength)
|
157
|
+
|
158
|
+
# Set up counterArray (Rows that are filled).
|
159
|
+
counterArray = Array.new(scheduleLength + 1, 0)
|
160
|
+
|
161
|
+
# Fill counterArray.
|
162
|
+
slots.each do | currentSlot |
|
163
|
+
currentRow = currentSlot.row
|
164
|
+
counterArray[currentRow] = counterArray[currentRow] + 1
|
165
|
+
end
|
166
|
+
|
167
|
+
# Update Weights.
|
168
|
+
slots.each do | currentSlot |
|
169
|
+
currentRow = currentSlot.row
|
170
|
+
currentPhase = currentSlot.phase
|
171
|
+
nightBoolean = currentSlot.isNight
|
172
|
+
peopleNeeded = Helpers.calculatePeopleNeeded(nightBoolean, currentPhase)
|
173
|
+
numFreePeople = counterArray[currentRow]
|
174
|
+
currentSlot.weight = currentSlot.weight*(12/numFreePeople)*peopleNeeded
|
175
|
+
end
|
176
|
+
|
177
|
+
return slots
|
178
|
+
end
|
179
|
+
|
180
|
+
# Update people, spreadsheet, and remove slots.
|
181
|
+
def self.weightPick(people, slots, graveyard, scheduleGrid)
|
182
|
+
|
183
|
+
# Remove winner from list.
|
184
|
+
winner = slots.shift;
|
185
|
+
|
186
|
+
# Update person information.
|
187
|
+
currentPersonID = winner.personID;
|
188
|
+
currentTime = winner.isNight;
|
189
|
+
|
190
|
+
if currentTime
|
191
|
+
people[currentPersonID].nightScheduled += 1
|
192
|
+
people[currentPersonID].nightFree -= 1
|
193
|
+
else
|
194
|
+
people[currentPersonID].dayScheduled += 1
|
195
|
+
people[currentPersonID].dayFree -= 1
|
196
|
+
end
|
197
|
+
|
198
|
+
# Establish Variables
|
199
|
+
currentRow = winner.row
|
200
|
+
currentCol = winner.col
|
201
|
+
tentCounter = 0
|
202
|
+
|
203
|
+
# Update Data
|
204
|
+
scheduleGrid[currentCol][currentRow].status = "Scheduled";
|
205
|
+
|
206
|
+
# Count number of scheduled tenters during winner slot.
|
207
|
+
i = 0
|
208
|
+
while i < scheduleGrid.length
|
209
|
+
if scheduleGrid[i][currentRow].status == "Scheduled"
|
210
|
+
tentCounter = tentCounter + 1
|
211
|
+
end
|
212
|
+
i += 1
|
213
|
+
end
|
214
|
+
|
215
|
+
# Determine how many people are needed.
|
216
|
+
peopleNeeded = Helpers.calculatePeopleNeeded(currentTime, winner.phase)
|
217
|
+
|
218
|
+
# Update Slots and Graveyard
|
219
|
+
if tentCounter >= peopleNeeded
|
220
|
+
graveyard[currentRow] = 1
|
221
|
+
j = 0
|
222
|
+
tempSlots = []
|
223
|
+
while j < slots.length
|
224
|
+
tempRow = slots[j].row
|
225
|
+
if tempRow != currentRow
|
226
|
+
tempSlots.push slots[j]
|
227
|
+
end
|
228
|
+
j += 1
|
229
|
+
end
|
230
|
+
slots = tempSlots
|
231
|
+
end
|
232
|
+
|
233
|
+
return people, slots, graveyard, scheduleGrid
|
234
|
+
end
|
235
|
+
end
|
data/lib/gthc/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gthc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aman Ibrahim
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-
|
12
|
+
date: 2019-12-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- ".travis.yml"
|
67
67
|
- CODE_OF_CONDUCT.md
|
68
68
|
- Gemfile
|
69
|
+
- Gemfile.lock
|
69
70
|
- LICENSE.txt
|
70
71
|
- README.md
|
71
72
|
- Rakefile
|
@@ -73,6 +74,10 @@ files:
|
|
73
74
|
- bin/setup
|
74
75
|
- gthc.gemspec
|
75
76
|
- lib/gthc.rb
|
77
|
+
- lib/gthc/olson.rb
|
78
|
+
- lib/gthc/olson/algorithm.rb
|
79
|
+
- lib/gthc/olson/helpers.rb
|
80
|
+
- lib/gthc/olson/weight.rb
|
76
81
|
- lib/gthc/version.rb
|
77
82
|
homepage: https://www.gthc.io/
|
78
83
|
licenses:
|
@@ -95,7 +100,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
100
|
- !ruby/object:Gem::Version
|
96
101
|
version: '0'
|
97
102
|
requirements: []
|
98
|
-
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.7.7
|
99
105
|
signing_key:
|
100
106
|
specification_version: 4
|
101
107
|
summary: GTHC Ruby Gem
|