Shuffle answers in multiple choice questions
[oweals/karmaworld.git] / karmaworld / apps / quizzes / create_quiz.py
index 4441e6c6165beeb192e310b6dc5f133a4e1919f5..a61d236cf1340b77c82e24ef2ab339b70dab041e 100644 (file)
+from copy import copy
 import random
-from karmaworld.apps.notes.models import Note
-from karmaworld.apps.quizzes.models import MultipleChoiceQuestion, Quiz, TrueFalseQuestion, MultipleChoiceOption, \
-    Keyword, FlashCardQuestion
+from karmaworld.apps.quizzes.models import Keyword
+
+
+class BaseQuizQuestion(object):
+
+    def question_type(self):
+        return self.__class__.__name__
+
+
+class MultipleChoiceQuestion(BaseQuizQuestion):
+    def __init__(self, question_text, choices):
+        self.question_text = question_text
+        self.choices = choices
+
+    def __unicode__(self):
+        return u"Multiple choice: {0}: {1}".format(self.question_text, ", ".join(map(str, self.choices)))
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
+
+class MultipleChoiceOption(object):
+    def __init__(self, text, correct):
+        self.text = text
+        self.correct = correct
+
+    def __unicode__(self):
+        return self.text
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
+
+class TrueFalseQuestion(BaseQuizQuestion):
+    def __init__(self, question_text, true):
+        self.question_text = question_text
+        self.true = true
+
+    def __unicode__(self):
+        return u"True or false: {0}".format(self.question_text)
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
+
+class MatchingQuestion(BaseQuizQuestion):
+    def __init__(self, question_text, left_column, right_column, left_right_mapping):
+        self.question_text = question_text
+        self.left_column = left_column
+        self.right_column = right_column
+        self.left_right_mapping = left_right_mapping
+
+    def rows(self):
+        return zip(self.left_column, self.right_column)
+
+    def __unicode__(self):
+        return u"Matching question: {0} / {1}".format(self.left_column, self.right_column)
+
+    def __str__(self):
+        return unicode(self)
+
+    def __repr__(self):
+        return str(self)
+
 
 KEYWORD_MULTIPLE_CHOICE = 1
 DEFINITION_MULTIPLE_CHOICE = 2
 KEYWORD_DEFINITION_TRUE_FALSE = 3
-FLASHCARD_KEYWORD_BLANK = 4
+KEYWORD_DEFINITION_MATCHING = 4
 GENERATED_QUESTION_TYPE = (
     KEYWORD_MULTIPLE_CHOICE,
     DEFINITION_MULTIPLE_CHOICE,
     KEYWORD_DEFINITION_TRUE_FALSE,
-    FLASHCARD_KEYWORD_BLANK,
+    KEYWORD_DEFINITION_MATCHING,
 )
 
 MULTIPLE_CHOICE_CHOICES = 4
 
 
-def _create_keyword_multiple_choice(keyword, keywords, quiz):
-    question_object = MultipleChoiceQuestion.objects.create(
-        question_text=keyword.definition,
-        quiz=quiz)
-
-    MultipleChoiceOption.objects.create(
-        text=keyword.word,
-        correct=True,
-        question=question_object)
+def _create_keyword_multiple_choice(keyword, keywords):
+    choices = [MultipleChoiceOption(text=keyword.word, correct=True)]
 
     for other_keyword in random.sample(keywords.exclude(id=keyword.id), MULTIPLE_CHOICE_CHOICES - 1):
-        MultipleChoiceOption.objects.create(
-            text=other_keyword.word,
-            correct=False,
-            question=question_object)
+        choices.append(MultipleChoiceOption(
+                       text=other_keyword.word,
+                       correct=False))
+
+    question_text = u'Pick the keyword to match "{d}"'.format(d=keyword.definition)
 
+    random.shuffle(choices)
 
-def _create_definition_multiple_choice(keyword, keywords, quiz):
-    question_object = MultipleChoiceQuestion.objects.create(
-        question_text=keyword.word,
-        quiz=quiz)
+    return MultipleChoiceQuestion(question_text, choices)
 
-    MultipleChoiceOption.objects.create(
-        text=keyword.definition,
-        correct=True,
-        question=question_object)
+
+def _create_definition_multiple_choice(keyword, keywords):
+    choices = [MultipleChoiceOption(text=keyword.definition, correct=True)]
 
     for other_keyword in random.sample(keywords.exclude(id=keyword.id), MULTIPLE_CHOICE_CHOICES - 1):
-        MultipleChoiceOption.objects.create(
-            text=other_keyword.definition,
-            correct=False,
-            question=question_object)
+        choices.append(MultipleChoiceOption(
+                       text=other_keyword.definition,
+                       correct=False))
+
+    question_text = u'Pick the definition to match "{w}"'.format(w=keyword.word)
+
+    random.shuffle(choices)
 
+    return MultipleChoiceQuestion(question_text, choices)
 
-def _create_keyword_definition_true_false(keyword, keywords, quiz):
+
+def _create_keyword_definition_true_false(keyword, keywords):
     true = random.choice((True, False))
 
     if true:
         definition = keyword.definition
     else:
-        other_keyword = keywords.exclude(id=keyword.id)
+        other_keyword = random.choice(keywords.exclude(id=keyword.id))
         definition = other_keyword.definition
 
-    question_text = 'Is the following a correct definition of {w}:<br/>{d}'. \
+    question_text = u'The keyword "{w}" matches the definition "{d}"'. \
         format(w=keyword.word, d=definition)
 
-    TrueFalseQuestion.objects.create(
-        text=question_text,
-        quiz=quiz,
-        true=true)
+    return TrueFalseQuestion(question_text, true)
+
 
+def _create_keyword_definition_matching(keyword, keywords):
+    question_keywords = [keyword]
+    question_keywords.extend(random.sample(keywords.exclude(id=keyword.id), MULTIPLE_CHOICE_CHOICES - 1))
 
-def _create_keyword_flashcard_blank(keyword, quiz):
-    FlashCardQuestion.objects.create(
-        definition_side=keyword.definition,
-        keyword_side=keyword.word,
-        quiz=quiz)
+    answer_mapping = {k.word: k.definition for k in question_keywords}
+    word_column = [k.word for k in question_keywords]
+    random.shuffle(word_column)
+    definition_column = [k.definition for k in question_keywords]
+    random.shuffle(definition_column)
 
+    question_text = u'Match the words with their definitions'
 
-def quiz_from_keywords(note_id):
-    note_object = Note.objects.get(id=note_id)
-    quiz_object = Quiz.objects.create(note=note_object, name=note_object.name)
-    keywords = Keyword.objects.filter(note=note_object)
+    return MatchingQuestion(question_text, left_column=word_column,
+                            right_column=definition_column, left_right_mapping=answer_mapping)
+
+
+def quiz_from_keywords(note):
+    keywords = Keyword.objects.filter(note=note).exclude(word__iexact='').exclude(definition__iexact='')
+    questions = []
+
+    if len(keywords) < 4:
+        return []
 
     for keyword in keywords:
         if keyword.word and keyword.definition:
             question_type = random.choice(GENERATED_QUESTION_TYPE)
 
             if question_type is KEYWORD_MULTIPLE_CHOICE:
-                _create_keyword_multiple_choice(keyword, keywords, quiz_object)
+                questions.append(_create_keyword_multiple_choice(keyword, keywords))
 
             elif question_type is DEFINITION_MULTIPLE_CHOICE:
-                _create_definition_multiple_choice(keyword, keywords, quiz_object)
+                questions.append(_create_definition_multiple_choice(keyword, keywords))
 
             elif question_type is KEYWORD_DEFINITION_TRUE_FALSE:
-                _create_keyword_definition_true_false(keyword, keywords, quiz_object)
+                questions.append(_create_keyword_definition_true_false(keyword, keywords))
+
+            elif question_type is KEYWORD_DEFINITION_MATCHING:
+                questions.append(_create_keyword_definition_matching(keyword, keywords))
 
-            elif question_type is FLASHCARD_KEYWORD_BLANK:
-                _create_keyword_flashcard_blank(keyword, quiz_object)
+    return questions