UNPKG

vue-course-requisite

Version:

Vue plugin for displaying, configuring, and modifying course pre and co requisites.

139 lines (117 loc) 4.93 kB
// The exposed API is used to validate a course prereqs / coreqs against some // set of courses distributed into terms // @param {Object} course: should be in the following form with optional prereqs / coreqs // refer to test/unit/fixtures/courses.js for examples of how the prereqs / coreqs // should be formed: // { // id: 1, // prereq: {/*...*/}, // coreq: {/*...*/} // } // @param {Number} index: the index in which the course is located in the terms // @param {Array} terms: should be formatted as follows: // [ // // term 1 with courses // [ // { id: 1 }, // { id: 2 }, // { id: 3 } // ], // // term 2 with courses // [ // { id: 4 }, // { id: 5 }, // { id: 6 } // ] // ] export default function (course, index, terms) { const prereq = JSON.parse(JSON.stringify(course.prereq || null)) const coreq = JSON.parse(JSON.stringify(course.coreq || null)) return { prereq: prereq ? validateRequisites(prereq, index, terms, 'prereq') : { code: 'valid' }, coreq: coreq ? validateRequisites(coreq, index, terms, 'coreq') : { code: 'valid' } } } // Validates a requisite tree // @param {Object} req: the requisite operand // @param {Number} index: index of original course occurance // @param {Array} terms: 2d array of courses by terms // @param {String} type: 'prereq' or 'coreq' function validateRequisites (req, index, terms, type) { const offendingCourses = [] const missingCourses = [] if (validateOperand(req, index, terms, type, offendingCourses, missingCourses)) { return { code: 'valid', reportedRequisite: req, offendingCourses, missingCourses } } else if (offendingCourses.length) { return { code: 'error', reportedRequisite: req, offendingCourses, missingCourses } } else { return { code: 'warning', reportedRequisite: req, offendingCourses, missingCourses } } } // Validates an operand based on the operand.type // @param {Object} operand: the requisite operand // @param {Number} index: index of original course occurance // @param {Array} terms: 2d array of courses by terms // @param {String} type: 'prereq' or 'coreq' // @param {Array} offendingCourses: array that will contain a list of courses that invalidate // the pre/co req (those that come on or after the term) // @param {Array} missingCourses: array that will contain a list of pre/co req courses that are missing function validateOperand (operand, index, terms, type, offendingCourses, missingCourses) { switch (operand.type) { case 'and': return operand.operands .map(o => validateOperand(o, index, terms, type, offendingCourses, missingCourses)) .every(b => b) case 'or': const ops = operand.operands.filter(o => ['or', 'and', 'course'].indexOf(o.type) > -1) return !ops.length || ops .map(o => validateOperand(o, index, terms, type, offendingCourses, missingCourses)) .some(b => b) case 'course': return validateCourseOperand(operand, index, terms, type, offendingCourses, missingCourses) default: return true } } // Validates a course operand // @param {Object} operand: the requisite operand // @param {Number} index: index of original course occurance // @param {Array} terms: 2d array of courses by terms // @param {String} type: 'prereq' or 'coreq' // @param {Array} offendingCourses: array that will contain a list of courses that invalidate // the pre/co req (those that come on or after the term) // @param {Array} missingCourses: array that will contain a list of pre/co req courses that are missing function validateCourseOperand (operand, index, terms, type, offendingCourses, missingCourses) { let validTerms = [] let invalidTerms = [] if (type === 'prereq') { validTerms = terms.slice(0, index) } if (type === 'coreq' || operand.concurrency_ind) { validTerms.push(terms[index]) } invalidTerms = remaining(terms, validTerms) let valid = validTerms.some(t => t.some(c => c.id === operand.course.id)) let offending = false if (!valid) { offending = invalidTerms.some(t => t.some(c => c.id === operand.course.id)) } if (valid) { operand.status = 'valid' } else if (offending) { operand.status = 'error' operand.statusMessage = `${type} is out of place` offendingCourses.push(operand.course) } else { operand.status = 'warning' operand.statusMessage = `${type} is missing` missingCourses.push(operand.course) } return valid } // Return the remaining array elements from a1 that are not in a2 // @param {Array} a1: array containing all elements // @param {Array} a2: array containing used elements function remaining (a1, a2) { return a1.filter(e => !a2.indexOf(e) > -1) }