-
Notifications
You must be signed in to change notification settings - Fork 0
/
student.rb
275 lines (211 loc) · 8.04 KB
/
student.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# Name: Payton Shaltis
# Project name: Assignment 1: EasyEnroll
# Description: An algorithm that determines the best college course enrollment strategy according to a set of student preferences and course constraints.
# Filename: student.rb
# Description: Contains the class implementation for representing Students.
# Last modified on: February 16, 2022
# Student class for representing students.
class Student
# Constants used for calculating student priority.
TOTAL_SEMESTERS = 8
MAX_REQUESTED_COURSES = 3
CLASS_PRIORITY = 10000
SEMESTER_PRIORITY = 1000
COURSES_TAKEN_PRIORITY = 10
SENIOR_MULTIPLIER = 4
JUNIOR_MULTIPLIER = 3
SOPHOMORE_MULTIPLIER = 2
FIRST_YEAR_MULTIPLIER = 1
# Read / write instance variables from CSV.
attr_accessor :student_id, :student_year, :courses_taken,
:semesters_left, :num_requests, :prefs
# Read / write instance variables NOT from CSV.
attr_accessor :enrolled_courses, :reasons, :priority
# Initializes the Student using a row from the CSV file.
def initialize(student_info, courses_hash)
# Variables straight from CSV file.
@student_id = student_info[0]
@student_year = student_info[1]
@semesters_left = student_info[3].to_i()
@num_requests = student_info[4].to_i()
# Values are formatted first via method calls.
@courses_taken = split_taken(student_info[2])
@prefs = merge_prefs(student_info[5], student_info[6], student_info[7], courses_hash)
# Variables NOT from the CSV file.
@enrolled_courses = []
@reasons = []
# Calculates the priority of the student.
calculate_priority()
end
# Returns an array of strings representing courses taken by
# the student in the past.
def split_taken(courses_taken)
if courses_taken
return courses_taken.split(", ")
else
return []
end
end
# Returns an array of strings representing course preferences
# of the student, omitting the "N/A"s.
def merge_prefs(pref1, pref2, pref3, courses_hash)
# We should ignore any duplicate preferences.
unfiltered = [pref1, pref2, pref3].uniq()
# Only keeps the non-"N/A" values.
filtered = []
unfiltered.each { |pref|
# Added as long as the user's preference is in the course constraints file.
if (pref) && (not pref == "N/A") && (courses_hash[pref])
filtered.push(pref)
end
}
return filtered
end
# Returns the priority level of a student based on the
# factors described in the assignment. Returns an integer.
def calculate_priority()
# A higher result indicates a higher priority for classes.
@priority = 0
# The first digit represents the class level.
case @student_year
when "Senior"
@priority += CLASS_PRIORITY * SENIOR_MULTIPLIER
when "Junior"
@priority += CLASS_PRIORITY * JUNIOR_MULTIPLIER
when "Sophomore"
@priority += CLASS_PRIORITY * SOPHOMORE_MULTIPLIER
when "First year student"
@priority += CLASS_PRIORITY * FIRST_YEAR_MULTIPLIER
end
# The second digit represents semesters taken.
@priority += SEMESTER_PRIORITY * (TOTAL_SEMESTERS - @semesters_left)
# The third and fourth digits represent courses taken.
@priority += COURSES_TAKEN_PRIORITY * @courses_taken.uniq().size()
# The fifth digit represents if the student selected the
# maximum number of courses that they could.
if (@prefs.size() == MAX_REQUESTED_COURSES) || (@courses_taken.uniq().size() + @prefs.size() == Course.total_courses())
@priority += MAX_REQUESTED_COURSES
else
@priority += (@prefs.size())
end
end
# Returns the number of overenrollments for all students.
def Student.overenrollments(students)
count = 0
students.each { |student|
difference = student.enrolled_courses().size() - student.num_requests()
count += (difference > 0) ? difference : 0
}
return count
end
# Returns true if the student is still enrolled in more classes
# than they initially requested (they are considered overenrolled).
def overenrolled()
@enrolled_courses.size() > @num_requests
end
# 'Enrolls' a student into courses, given as an array or a single
# string. Adds the course to the array of courses the student is
# currently enrolled in. A reference to this student is also stored
# in the enrolled_students array within that class.
def enroll(courses, courses_hash)
# Courses is an array.
if courses.class() == Array
courses.each { |course|
@enrolled_courses.push(course)
# Add reference to the course.
courses_hash[course].enrolled_students().push(self)
}
# Courses is a string.
elsif courses.class() == String
@enrolled_courses.push(courses)
# Add reference to the course.
courses_hash[courses].enrolled_students().push(self)
end
end
# 'Unenrolls' a student from a course if they are still
# overenrolled and enrolled in this course. Returns true
# if the student was unenrolled, false if the student was
# not. Different from being 'kicked'; unenrolling is used
# to manage students who are enrolled in too many courses
# from the start.
def unenroll(course, courses_hash)
# Ensure the student is still overenrolled and in this course
if overenrolled() && @enrolled_courses.include?(course)
# Remove the course and student from one another.
@enrolled_courses.delete(course)
courses_hash[course].enrolled_students().delete(self)
# Indicates that the student was unenrolled.
return true
end
# Indicates that this method call had no effect.
return false
end
# 'Kicks' a student from a course. This action is performed
# when a course is above its total maximum number of students,
# or the student is being swapped out for another student.
def kick(course, courses_hash)
# Remove the course and student from one another.
@enrolled_courses.delete(course)
courses_hash[course].enrolled_students().delete(self)
end
# Returns an array of values to be written to CSV files.
def to_csv()
enrolled_string = ""
reason_string = ""
# Generate the string of enrolled courses or "None".
@enrolled_courses.each { |course|
enrolled_string += "#{course}, "
}
if not enrolled_string == ""
enrolled_string = enrolled_string.chop().chop()
else
enrolled_string = "None"
end
# Generate the string of reasons or "N/A".
@reasons.each { |reason|
reason_string += "#{reason} "
}
if not reason_string == ""
reason_string = reason_string.chop()
else
reason_string = "N/A"
end
# Return the CSV-formatted array representation of a Student.
return [@student_id, enrolled_string, reason_string]
end
# Sorts an array of students in place from lowest priority to
# highest priority using quicksort.
def Student.sort_students_lth(students, front, back)
# Recursive case for quicksort.
if front < back
mid = Student.partition(students, front, back)
Student.sort_students_lth(students, front, mid - 1)
Student.sort_students_lth(students, mid + 1, back)
end
end
# Partition helper method for quicksortings students.
def Student.partition(students, front, back)
pivot = students[back] # Pivot starts as the last student.
index = front; # Begin out linear scan at the front.
pivot_index = front; # Position of pivot starts at the front.
# Pseudo-sort of the current partition.
while index < back
# Comparison based off of student priorities.
if students[index].priority <= pivot.priority
# Swap the two students.
temp = students[index]
students[index] = students[pivot_index]
students[pivot_index] = temp
# Increment the pivot_index position.
pivot_index += 1
end
index += 1
end
# Moves 'pivot' to its correct position
temp = students[pivot_index]
students[pivot_index] = students[back]
students[back] = temp
# Pivot is in its correct position.
return pivot_index
end
end