Last active
October 28, 2018 12:00
-
-
Save latviancoder/34608fe785b62f49aa63c69dbfa051dd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as _ from 'lodash'; | |
class Course { | |
students = []; | |
quizzes = []; | |
constructor(name, teacher) { | |
this.name = name; | |
this.teacher = teacher; | |
} | |
getTeacher() { | |
return this.teacher; | |
} | |
addStudent(student) { | |
this.students.push(student); | |
} | |
hasStudent(student) { | |
return this.students.includes(student); | |
} | |
getStudents() { | |
return this.students; | |
} | |
getQuizzes() { | |
return this.quizzes; | |
} | |
addQuiz(name, questions) { | |
const quiz = new Quiz(name, questions); | |
this.quizzes.push(quiz); | |
return quiz; | |
} | |
calculateGrade(student) { | |
let grade = 0; | |
for (let quiz of this.quizzes) { | |
grade += student.getQuizAnswers(quiz).calculateGrade(); | |
} | |
return grade; | |
} | |
} | |
class Quiz { | |
constructor(name, questions) { | |
this.name = name; | |
this.questions = questions; | |
} | |
getQuestions() { | |
return this.questions; | |
} | |
} | |
class Student { | |
courses = []; | |
quizAnswers = new Map(); | |
constructor(name) { | |
this.name = name; | |
} | |
getName() { | |
return this.name; | |
} | |
enroll(course) { | |
this.courses.push(course); | |
course.addStudent(this); | |
} | |
getCourses() { | |
return this.courses; | |
} | |
answerQuiz(quiz, answers) { | |
const quizAnswers = new QuizAnswers(this, quiz, answers); | |
this.quizAnswers.set(quiz, quizAnswers); | |
return quizAnswers; | |
} | |
getQuizAnswers(quiz) { | |
return this.quizAnswers.get(quiz); | |
} | |
} | |
class QuizAnswers { | |
constructor(student, quiz, answers) { | |
this.student = student; | |
this.quiz = quiz; | |
this.answers = answers; | |
} | |
getAnswers() { | |
return this.answers; | |
} | |
getAnsweredQuestionsCount() { | |
return Object.keys(this.answers).length; | |
} | |
calculateGrade() { | |
let questions = this.quiz.getQuestions(); | |
let totalScore = 0; | |
// `answers` is an object because partial submissions can be made | |
// unanswered questions are not scored | |
_.forOwn(this.answers, function(answers, questionIndex) { | |
let questionScore = 0; | |
const correctAnswers = questions[questionIndex].correctAnswers; | |
// partial scoring is enabled - selecting wrong option will result in negative score | |
for (const answer of answers) { | |
if (correctAnswers.includes(answer)) { | |
questionScore += 1; | |
} else { | |
questionScore -= 1; | |
} | |
} | |
if (questionScore > 0) { | |
totalScore += (questionScore/correctAnswers.length); | |
} | |
}); | |
// Minimum grade - 0 | |
// Maximum grade - 10 | |
// Because unanswered questions are not scored we do not divide by overall questions count | |
return Math.floor(totalScore/this.getAnsweredQuestionsCount() * 10); | |
} | |
} | |
class Teacher { | |
courses = []; | |
constructor(name) { | |
this.name = name; | |
} | |
getName() { | |
return this.name; | |
} | |
getCourses() { | |
return this.courses; | |
} | |
addCourse(name) { | |
const course = new Course(name, this); | |
this.courses.push(course); | |
return course; | |
} | |
} | |
export { | |
Student, | |
Teacher, | |
Course, | |
Quiz, | |
QuizAnswers | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Student, Teacher} from './index'; | |
// I assumed nobody is going to try to break this stuff and didn't add almost any error handling | |
let smith; | |
let bob; | |
beforeEach(() => { | |
smith = new Teacher('Mr. Smith'); | |
bob = new Student('Bob'); | |
}); | |
test('can create instances of teacher', () => { | |
expect(smith).toBeInstanceOf(Teacher); | |
expect(smith.getName()).toBe('Mr. Smith'); | |
}); | |
test('can create instances of student', () => { | |
expect(bob).toBeInstanceOf(Student); | |
expect(bob.getName()).toBe('Bob'); | |
}); | |
describe('enrollment and quizzes', () => { | |
let smithBiology; | |
let quiz; | |
beforeEach(() => { | |
// For the sake of simplicity I assume 1 course can only have 1 teacher, but teachers can have many courses | |
smithBiology = smith.addCourse('Biology'); | |
bob.enroll(smithBiology); | |
// I assumed teachers can't assign different quizzes to different students in course | |
// Quizzes are automatically applied to everyone in course | |
quiz = smithBiology.addQuiz( | |
'Quiz number 1', | |
[ | |
{ | |
question: 'Another question?', | |
options: [ | |
'Option 0', 'Option 1', 'Option 2' | |
], | |
correctAnswers: [1] | |
}, | |
{ | |
question: 'What is round?', | |
options: [ | |
'Ball', 'Knife', 'Sun', 'Orange', 'Car' | |
], | |
correctAnswers: [0, 2, 3] | |
} | |
] | |
); | |
}); | |
test('teacher can create courses', () => { | |
expect(smithBiology.getTeacher()).toBe(smith); | |
expect(smith.getCourses()).toHaveLength(1); | |
expect(smith.getCourses()).toContain(smithBiology); | |
}); | |
test('teacher can create multiple courses', () => { | |
smith.addCourse('Algebra'); | |
expect(smith.getCourses()).toHaveLength(2); | |
expect(smith.getCourses()).toContain(smithBiology); | |
}); | |
test('students can enroll into courses', () => { | |
expect(bob.getCourses()).toContain(smithBiology); | |
expect(smithBiology.getStudents()).toContain(bob); | |
}); | |
test('quizzes can be added to the course', () => { | |
expect(smithBiology.getQuizzes()).toHaveLength(1); | |
expect(smithBiology.getQuizzes()).toContain(quiz); | |
}); | |
describe('grading quizzes', () => { | |
test('maximum score is 10', () => { | |
const bobQuizAnswers = bob.answerQuiz(quiz, { | |
0: [1], | |
1: [0, 2, 3] | |
}); | |
expect(bobQuizAnswers.calculateGrade()).toBe(10); | |
}); | |
test('minimum score is 0', () => { | |
const bobQuizAnswers = bob.answerQuiz(quiz, { | |
0: [0], | |
1: [1] | |
}); | |
expect(bobQuizAnswers.calculateGrade()).toBe(0); | |
}); | |
test('partial submissions can be made, unanswered questions are not graded', () => { | |
const bobQuizAnswers = bob.answerQuiz(quiz, { | |
1: [0, 2, 3] | |
}); | |
expect(bobQuizAnswers.calculateGrade()).toBe(10); | |
}); | |
test('student can be graded in course', () => { | |
bob.answerQuiz(quiz, { | |
0: [1], | |
1: [0, 1, 3] | |
}); | |
expect(smithBiology.calculateGrade(bob)).toBe(6); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment