-
Notifications
You must be signed in to change notification settings - Fork 74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/quiz module #142
base: develop
Are you sure you want to change the base?
Feature/quiz module #142
Changes from all commits
5c935a1
ab12970
5c267a7
e17f450
2b36aa7
42940bc
ee82cbf
3dfe77c
34d6748
dbfd93a
de75f0d
c1d82fd
febfb5f
867f44d
e0bba16
bec302f
86f2f51
e1e1953
2660316
5b5a450
ccd87f8
7f02fcc
44674f1
361f2ee
62e3ffb
006058d
39270a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
Schema = {}; | ||
QuizzesSchema = {}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
Questions = new Mongo.Collection('questions'); | ||
|
||
QuestionSchema = new SimpleSchema({ | ||
id: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is unnecessary, as all MongoDB documents have an |
||
type: String, | ||
optional: false, | ||
unique: true | ||
}, | ||
questionType: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This field should probably have some |
||
type:String, | ||
optional: false | ||
}, | ||
quizId: { | ||
type:String, | ||
optional: false | ||
}, | ||
title: { | ||
type:String, | ||
optional: false | ||
}, | ||
description : { | ||
type:String, | ||
optional: false | ||
}, | ||
numberOfOptions: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider re-organizing your options schema as follows: E.g. "options": {
type: [Object],
max: 8,
},
"options.$.title": {
type: String
},
"options.$.isCorrectAnswer": {
type: Boolean
} |
||
|
||
type: Number, | ||
optional: true, | ||
max: 10, | ||
min: 2 | ||
}, | ||
optionTitles: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can remove this field, with the above refactoring. |
||
label: "Options", | ||
type: [Object], | ||
optional: false, | ||
autoform: { | ||
type: "radio-with-text-input", | ||
options: function(){ | ||
var optionsArray = []; | ||
|
||
for ( var i=0; i < 8; i++){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this loop hard-coded to 8? You can use the |
||
var option = Quiz.generateAnswerOption("", false, i) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this line do. Please add comment in plain English. |
||
optionsArray.push(option); | ||
} | ||
return optionsArray; | ||
} | ||
}, | ||
custom: function() { | ||
// This custom function renders an error, if this field is not equal to | ||
// the new Password field supplied in the form. | ||
var titlesValid = true; | ||
var selectionFound = false; | ||
for (var i = 0; i < this.value.length; i++){ | ||
var obj = this.value[i]; | ||
if (!obj.title){ | ||
titlesValid = false; | ||
return "invalidQuestionTitles"; | ||
} | ||
if (obj.isSelected){ | ||
selectionFound = true; | ||
} | ||
} | ||
if (!selectionFound){ | ||
return "invalidQuestionSelection"; | ||
} | ||
} | ||
}, | ||
|
||
"optionTitles.$.title": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This field is unnecessary, if you refactor the "options" object. E.g. "options.$.title": {
type: String
}, |
||
type:String, | ||
optional: false | ||
}, | ||
|
||
"optionTitles.$.isSelected": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This field is unnecessary if you simplify the "options.$.isCorrectAnswer": {
type: Boolean
} |
||
type:Boolean, | ||
optional: false | ||
}, | ||
"optionTitles.$.index":{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This field may be unnecessary, since JavaScript arrays are natively indexed. |
||
type:Number, | ||
optional: false | ||
}, | ||
|
||
|
||
options:{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please refactor this object as indicated: "options": {
type: [Object],
max: 8,
},
"options.$.title": {
type: String
},
"options.$.isCorrectAnswer": {
type: Boolean
} |
||
label: "Options", | ||
type: [Object], | ||
} | ||
}) | ||
|
||
|
||
Questions.attachSchema(QuestionSchema); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
Quizzes = new Mongo.Collection('quizzes'); | ||
|
||
QuizzesSchema.AnswerOptionSchema = new SimpleSchema({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may be redundant, as individual options may be marked as |
||
title: { | ||
type:String, | ||
optional: true, | ||
defaultValue: "" | ||
}, | ||
isCorrect: { | ||
type: Boolean, | ||
defaultValue: false | ||
}, | ||
}); | ||
|
||
//This schema will validate the initial creation of a quiz | ||
QuizzesSchema.QuizzesSchema = new SimpleSchema({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Define this schema directly in the Quizzes object: Quizzes.schema = new SimpleSchema({
...
}); |
||
title: { | ||
type:String, | ||
label: "Quiz Title", | ||
min: 4, | ||
max: 140 | ||
}, | ||
questions: { | ||
type: [Object], | ||
optional: true | ||
}, | ||
|
||
lessonID: { | ||
type:String, | ||
}, | ||
}); | ||
|
||
Quizzes.attachSchema(QuizzesSchema.QuizzesSchema); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
SimpleSchema.messages({ | ||
"passwordMismatch": "Passwords do not match" | ||
"passwordMismatch": "Passwords do not match", | ||
"invalidQuestionSettings": "Invalid question settings", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to remember to internationalize these strings, so they can be translated. |
||
"invalidQuestionTitles": "All options should be filled in", | ||
"invalidQuestionSelection": "Please select an answer" | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
Quiz = function(){ | ||
var quiz = this; | ||
this.addNewQuestion = function(val){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This type of functionality is handled in a simple manner by embracing AutoForm. |
||
//check if the question already exists | ||
if (quiz.questions == undefined){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
quiz.questions = []; | ||
} | ||
for (var q in quiz.questions){ | ||
|
||
}; | ||
|
||
quiz.questions.push(val); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this in to the |
||
}; | ||
|
||
}; | ||
|
||
Quiz.convertToQuizObject = function(object){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AutoForm and SimpleSchema handle this automatically. |
||
|
||
if (object == undefined) return; | ||
var quiz = new Quiz(); | ||
|
||
quiz._id = object._id; | ||
quiz.title = object.title; | ||
quiz.lessonID = object.lessonID; | ||
quiz.questions = object.questions; | ||
quiz.userAttempts = object.userAttempts; | ||
|
||
return quiz; | ||
}; | ||
|
||
Quiz.generateQuestion = function(questionType, quizId ){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AutoForm and SimpleSchema handle this automatically. |
||
var question = new Object(); | ||
question.quizId = quizId; | ||
question.id = Random.id(); //assign an id to the question - need this for checking and validating answers | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MongoDB automatically assigns an |
||
question.questionType = questionType; | ||
|
||
//if the question type is true-or-false, populate the answer options | ||
if (question.questionType == QuizOptions.TRUE_OR_FALSE){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use SimpleSchema to dynamically define what field to display, based on the |
||
question.optionTitles = []; | ||
var trueOption = Quiz.generateAnswerOption("True", false, 0); | ||
|
||
question.optionTitles.push(trueOption); | ||
var falseOption = Quiz.generateAnswerOption("False", false, 1); | ||
question.optionTitles.push(falseOption); | ||
}; | ||
|
||
question.description = ""; | ||
question.title = ""; | ||
question.options = []; | ||
return question; | ||
}; | ||
|
||
Quiz.generateAnswerOption = function (title, isSelected, index){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SimpleSchema and AutoForm handle this simply and automatically. |
||
var option = {}; | ||
option.title = title; | ||
option.isSelected = isSelected; | ||
option.index = index; | ||
|
||
return option | ||
} | ||
|
||
|
||
Object.defineProperty(Quiz, "_title", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SimpleSchema handles this automatically. Then, we use plain JavaScript to access object properties. |
||
get: function title(){ | ||
return this._title; | ||
}, | ||
set: function title(val){ | ||
this._title = val; | ||
} | ||
}); | ||
Object.defineProperty(Quiz, "_questions", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SimpleSchema handles this automatically. Then, we use plain JavaScript to access object properties. |
||
get: function question(){ | ||
return this._questions; | ||
}, | ||
set: function questions(val){ | ||
this._questions = val; | ||
} | ||
}); | ||
|
||
Object.defineProperty(Quiz, "_lessonID", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SimpleSchema handles this automatically. Then, we use plain JavaScript to access object properties. |
||
get: function lessonID(){ | ||
return this._lessonIDs | ||
}, | ||
set: function lessonID(val){ | ||
this._lessonID = val; | ||
} | ||
}); | ||
|
||
Object.defineProperty(Quiz, "_userAttempts", { //an array of objects containig user ids, date and result | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should move const UserAttempts = new Mongo.collection("userAttempts");
UserAttempts.schema = new SimpleSchema({
userId: {
type: String,
regEx: SimpleSchema.RegEx.Id
},
quizId: {
type: String,
regEx: SimpleSchema.RegEx.Id
},
userAttempts: {
type: [Object] // Hold an array of all user attempts
},
"userAttempts.$.successful": {
type: Boolean // true if user passes, false if user fails
}
}); |
||
get: function userAttempts(){ | ||
return this._userAttempts; | ||
}, | ||
set: function userAttempts(val){ | ||
this._userAttempts = val; | ||
} | ||
}) | ||
|
||
|
||
|
||
|
||
QuizOptions = {}; | ||
|
||
QuizOptions.MULTIPLE_CHOICE_SINGLE_ANSWER = "Multiple Choice - single answer"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of this can probably be done at the schema level. |
||
QuizOptions.MULTIPLE_CHOICE_MULTIPLE_ANSWERS = "Multiple Choice - multiple answers"; | ||
QuizOptions.TRUE_OR_FALSE = "True or False"; | ||
QuizOptions.QUESTION_TYPES = | ||
[ QuizOptions.MULTIPLE_CHOICE_SINGLE_ANSWER, | ||
QuizOptions.MULTIPLE_CHOICE_MULTIPLE_ANSWERS, | ||
QuizOptions.TRUE_OR_FALSE | ||
]; | ||
|
||
|
||
|
||
QuizQuestion = function() {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SimpleSchema provides the structure for Questions, while JavaScript provides simple access to the object properties without the need for getters or setters. |
||
Object.defineProperty(QuizQuestion, "_questionType", { | ||
get: function questionType() { | ||
return this._quiztype; | ||
}, | ||
set: function questionType(val) { | ||
this._quiztype = val; | ||
} | ||
}); | ||
|
||
//array of lesson IDs - the quiz can be used in more than one lesson | ||
Object.defineProperty(QuizQuestion, "_quizId", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by SimpleSchema. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The quizModel class was left in the code by mistake (it was used in the previous implementation - prior to the SimpleSchema and autoform integration). I'll remove the file. |
||
get: function quizId(){ | ||
return this._quizId | ||
}, | ||
set: function quizId(val){ | ||
this._lessonIDs = val; | ||
} | ||
}); | ||
|
||
Object.defineProperty(QuizQuestion, "_title", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by SimpleSchema. JavaScript properties are accessible on returned MongoDB document objects. |
||
get: function title(){ | ||
return this._title; | ||
}, | ||
set: function title(val){ | ||
this._title = val; | ||
} | ||
}); | ||
|
||
Object.defineProperty(QuizQuestion, "_description", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by SimpleSchema. JavaScript properties are accessible on returned MongoDB document objects. |
||
get: function description(){ | ||
return this._description; | ||
}, | ||
set: function description(val){ | ||
this._description = val; | ||
} | ||
}); | ||
|
||
Object.defineProperty(QuizQuestion, "_options", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by SimpleSchema. JavaScript properties are accessible on returned MongoDB document objects. |
||
get: function options(){ | ||
return this._options; | ||
}, | ||
set: function options(val){ | ||
this._questions = val; | ||
} | ||
}); | ||
|
||
Object.defineProperty(QuizQuestion, "_saved",{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by SimpleSchema. JavaScript properties are accessible on returned MongoDB document objects. |
||
get: function saved(){ | ||
return this._saved; | ||
}, | ||
set: function saved(val){ | ||
this._saved = val; | ||
} | ||
}); | ||
|
||
Object.defineProperty(QuizQuestion, "_answered", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by SimpleSchema. JavaScript properties are accessible on returned MongoDB document objects. |
||
get: function answered(){ | ||
return this._answered; | ||
}, | ||
set: function answered(val){ | ||
this._answered = val; | ||
} | ||
}) | ||
|
||
Object.defineProperty(QuizQuestion, "_isMultipleAnswer", { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is handled by SimpleSchema. JavaScript properties are accessible on returned MongoDB document objects. |
||
get: function isMultipleAnswer(){ | ||
return this._questionType == QuizOptions.MULTIPLE_CHOICE_MULTIPLE_ANSWERS; | ||
} | ||
}); | ||
|
||
Object.defineProperty(QuizQuestion, "_isSingleAnswer", { | ||
get: function isSingleAnswer(){ | ||
return this._questionType == QuizOptions.MULTIPLE_CHOICE_SINGLE_ANSWER; | ||
} | ||
}); | ||
|
||
Object.defineProperty(QuizQuestion, "_isTrueOrFalse", { | ||
get: function isTrueOrfalse(){ | ||
return this._questionType == QuizOptions.TRUE_OR_FALSE; | ||
} | ||
}) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,20 @@ Template.registerHelper('editMode', function () { | |
// get edit mode session variable | ||
return Session.get('editMode'); | ||
}); | ||
|
||
Template.registerHelper('isEditingCurrentCourse', function() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea to move this to a general Template helper, since it was duplicated. |
||
// Get reference to current router | ||
var router = Router.current(); | ||
|
||
// Get Course ID from router | ||
var currentCourseId = router.params._id; | ||
|
||
// Get value of editing course session variable | ||
var editingCourseId = Session.get('editingCourseId') | ||
|
||
// See if user is editing current course | ||
var editingCurrentCourse = (editingCourseId === currentCourseId); | ||
|
||
// return true if user is editing this course | ||
return editingCurrentCourse; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Attach this schema to the
Questions
object: