Skip to content

Commit 66df680

Browse files
committed
Made lab classes finally work correctly. Fixes #49 and Closes #68
1 parent e1d6598 commit 66df680

File tree

4 files changed

+184
-7
lines changed

4 files changed

+184
-7
lines changed

api/generate.php

Lines changed: 177 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,104 @@ function timeStringToMinutes($str) {
184184
return $hour * 60 + $minute;
185185
}
186186

187+
/**
188+
* Checks if a section is a special section (lab/studio/etc)
189+
* @param $courseInfo
190+
* @return int
191+
*/
192+
function isSpecialSection($courseInfo) {
193+
return preg_match('/[A-Z]\d{0,2}$/', $courseInfo['courseNum']) === 1;
194+
}
195+
196+
/**
197+
* Returns a cleaned course number, free of special sections or designators
198+
* @param $courseInfo
199+
* @return mixed
200+
*/
201+
function getCleanCourseNum($courseInfo) {
202+
$matches = array();
203+
if(preg_match('/^(.*?)-?(?:[A-Z]\d{0,2})$/', $courseInfo['courseNum'], $matches) === 1) {
204+
return $matches[1];
205+
} else {
206+
return $courseInfo['courseNum'];
207+
}
208+
}
209+
210+
/**
211+
* Prunes invalid schedules based on courseGroups
212+
* @param $schedules
213+
* @param $courseGroups
214+
* @return array
215+
*/
216+
function pruneSpecialCourses($schedules, $courseGroups) {
217+
218+
// The array of schedules that meet all course requirements
219+
$validSchedules = array();
220+
221+
// Loop through each possible schedule
222+
foreach($schedules as $schedule) {
223+
224+
// Flattened schedule [courseNum => <value>] where <value> is:
225+
// false: no co-requirements
226+
// true: is a co-requirement
227+
// string[]: list of possible requirements
228+
$flattenedSchedule = array();
229+
230+
// Loop through each course
231+
foreach($schedule as $course) {
232+
233+
$cleanCourseNum = getCleanCourseNum($course);
234+
235+
// This course has selected labs or is a lab
236+
if(array_key_exists($cleanCourseNum, $courseGroups) && count($courseGroups[$cleanCourseNum]) > 0) {
237+
if(!isSpecialSection($course)) {
238+
239+
// Set the course requirement as an array of courseNum strings
240+
$flattenedSchedule[$course['courseNum']] = array_keys($courseGroups[$cleanCourseNum]);
241+
} else {
242+
$flattenedSchedule[$course['courseNum']] = true;
243+
}
244+
} else {
245+
$flattenedSchedule[$course['courseNum']] = false;
246+
}
247+
}
248+
249+
$scheduleMeetsRequirements = true;
250+
// Loop through the flatten schedules
251+
foreach($flattenedSchedule as $courseNum => $courseRequirements) {
252+
253+
// Check if course has requirements
254+
if(is_array($courseRequirements)) {
255+
$courseMeetsRequirement = false;
256+
257+
// Loop through the requirements, checking if the schedule contains AT LEAST one required course
258+
foreach($courseRequirements as $specialCourseNum) {
259+
if(array_key_exists($specialCourseNum, $flattenedSchedule)) {
260+
$courseMeetsRequirement = true;
261+
break;
262+
}
263+
}
264+
265+
// "AND" the previous results with the current one
266+
$scheduleMeetsRequirements = $scheduleMeetsRequirements && $courseMeetsRequirement;
267+
268+
// Don't bother checking other courses if the schedule already does not meet requirements
269+
if(!$scheduleMeetsRequirements) {
270+
continue;
271+
}
272+
}
273+
}
274+
275+
// Add this to the valid schedules if it meets all requirements
276+
if($scheduleMeetsRequirements) {
277+
$validSchedules[] = $schedule;
278+
}
279+
}
280+
281+
// Return the resulting array of all schedules that met co-requirements
282+
return $validSchedules;
283+
}
284+
187285
////////////////////////////////////////////////////////////////////////////
188286
// MAIN EXECUTION
189287

@@ -291,20 +389,93 @@ function timeStringToMinutes($str) {
291389
// GET MATCHING SCHEDULES
292390
case "getMatchingSchedules":
293391
// Process the list of courses that were selected
392+
393+
// Keep track of grouped classes by both clean course name (sections) and by course input index
394+
/**
395+
* array(string {cleanCourseNum} => array(string {courseNum} => {courseInfo array})))
396+
*/
397+
$courseGroups = array();
398+
399+
/**
400+
* array(int {course input index} => array(integer {courseId} => array(string {courseNum} => {courseInfo array})))
401+
*/
402+
$courseGroupsByCourseId = array();
403+
294404
$courseSet = array();
295405
for($i = 1; $i <= $_POST['courseCount']; $i++) { // It's 1-indexed... :[
296406
// Iterate over the courses in that course slot
297407
if(!isset($_POST["courses{$i}Opt"])) { continue; }
298408
$courseSubSet = array();
409+
$courseGroupsByCourseId[$i] = array();
299410
foreach($_POST["courses{$i}Opt"] as $course) {
411+
300412
// Do a query to get the course specified
301-
$courseInfo = getCourseBySectionId($course);
302-
$courseInfo['courseIndex'] = $i;
303-
$courseSubSet[] = $courseInfo;
413+
$courseInfo = getCourseBySectionId($course);
414+
415+
// courseIndex is only used by the frontend UI to determine what color/grouping to use
416+
$courseInfo['courseIndex'] = $i;
417+
418+
// Remove the potential special indicators from the end of the courseNum
419+
$cleanCourseNum = getCleanCourseNum($courseInfo);
420+
421+
// Create the group if it does not already exist
422+
if(!array_key_exists($cleanCourseNum, $courseGroups)) {
423+
$courseGroups[$cleanCourseNum] = array();
424+
}
425+
426+
// Create the group by index and course id. Can probably ignore courseId, but will be eventually useful
427+
if(!array_key_exists($courseInfo['courseId'], $courseGroupsByCourseId[$i])) {
428+
$courseGroupsByCourseId[$i][$courseInfo['courseId']] = array();
429+
}
430+
431+
// Check if the section is a special course: courseNum ending in a letter, then one or two digits
432+
if(isSpecialSection($courseInfo)) {
433+
434+
if(!array_key_exists($courseInfo['courseNum'], $courseGroups[$cleanCourseNum])) {
435+
436+
// Add this course to its group
437+
$courseGroups[$cleanCourseNum][$courseInfo['courseNum']] = $courseInfo;
438+
}
439+
440+
if(!array_key_exists($courseInfo['courseNum'], $courseGroupsByCourseId[$i][$courseInfo['courseId']])) {
441+
442+
// Add this course to its group by course id
443+
$courseGroupsByCourseId[$i][$courseInfo['courseId']][$courseInfo['courseNum']] = $courseInfo;
444+
}
445+
446+
} else {
447+
448+
// This is a normal class, it can be added like normal to the sub set
449+
$courseSubSet[] = $courseInfo;
450+
}
451+
}
452+
453+
// Add the normal subset to the main set
454+
if(count($courseSubSet) > 0) {
455+
$courseSet[] = $courseSubSet;
304456
}
305-
$courseSet[] = $courseSubSet;
306457
}
307-
458+
459+
460+
// Loop through each course groups' courses and flatten the array
461+
if(count($courseGroups) > 0) {
462+
foreach($courseGroupsByCourseId as $courseGroupsByIndex) {
463+
$specialCourseSubSet = array();
464+
foreach($courseGroupsByIndex as $courseGroup) {
465+
// Get each special course
466+
foreach ($courseGroup as $specialCourse) {
467+
$specialCourseSubSet[] = $specialCourse;
468+
}
469+
}
470+
471+
// Add any special courses for this index to the main courseSet.
472+
if (count($specialCourseSubSet) > 0) {
473+
$courseSet[] = $specialCourseSubSet;
474+
}
475+
}
476+
}
477+
478+
// Set the courseIndex for the remaining nonCourse/noCourse routines
308479
$courseIndex = $i;
309480

310481
// Process the list of nonCourse Items
@@ -358,7 +529,7 @@ function timeStringToMinutes($str) {
358529
// Generate valid schedules, and include the errors if we're being verbose
359530
$results = array();
360531
if(!empty($courseSet)) {
361-
$results['schedules'] = generateSchedules($courseSet, $nonCourseSet, $noCourseSet);
532+
$results['schedules'] = pruneSpecialCourses(generateSchedules($courseSet, $nonCourseSet, $noCourseSet), $courseGroups);
362533
} else {
363534
$results['schedules'] = array(array());
364535
}

assets/src/modules/sm/App/controllers/AppController.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ angular.module('sm').controller("AppController", function($scope, localStorage,
3636
alert_generateFeatures: true,
3737
alert_searchFeatures: true,
3838
alert_browseFeatures: true,
39+
alert_labClasses: true,
3940
action_generateSchedules: false,
4041
};
4142

assets/src/modules/sm/Generate/templates/generate.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<form novalidate id="scheduleForm" name="schedule" class="container">
22
<div class="row">
33
<div class="col-md-8 clearfix">
4+
<div class="alert alert-success" ng-show="state.ui.alert_labClasses">
5+
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="state.ui.alert_labClasses = false"><i class="fa fa-times"></i></button>
6+
<strong>NEW!</strong> Lab classes and other sections should now be paired with their respective sections. Just enter in classes with labs and everything should work as you expected. Please <a target="_blank" href="https://github.com/ComputerScienceHouse/schedulemaker/issues">report any issues</a> that you find!
7+
8+
</div>
49
<div class="alert alert-info" ng-show="state.ui.alert_generateFeatures">
510
<button type="button" class="close" data-dismiss="alert" aria-hidden="true" ng-click="state.ui.alert_generateFeatures = false"><i class="fa fa-times"></i></button>
611
Use a comma to separate courses to see which course fits your schedule better. Add courses from the Browse or Search page to your schedule as well so you can easily create schedule combinations from anywhere! Also, check out the <a ui-sref="help">help</a> page for new keyboard shortcuts.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "schedulemaker",
3-
"version": "3.0.10",
3+
"version": "3.0.11",
44
"private": true,
55
"description": "A course database lookup tool and schedule building web application for use at Rochester Institute of Technology.",
66
"main": "index.php",

0 commit comments

Comments
 (0)