Welcome, guest | Sign In | My Account | Store | Cart

Before discovering http://quizlet.com/, the following program was developed for running custom quizzes to help with studying for college courses. The program is not very advanced, but it works reasonably well for what it was designed to do. If the program were developed further, it would need greater capabilities than it currently has and would require a secondary system for actually creating the quizzes (currently, they are hand-typed). Quiz Me could be a starting point for anyone who wishes to actually write a program such as this and inspire others to write much better programs than what this recipe currently offers.

The classes in this module take a XML node tree of a test/quiz bank and converts it into a definite, usable structure for creating a collection of question and answers that can be asked from different categories. UnitTest, Chapter, and Section represent divisions of a book (such as from history) and specify several different (inflexible) tiers in which questions may be placed. The Category function automatically determines what types of questions are being asked and creates appropriate objects that contain Fact objects from which Questions and Answers are generated. True_Or_False, Multiple_Choice, and Matching questions types are supported.

Python, 148 lines
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
####################
# source/quizcore.py
####################

import random

from . import testbank

################################################################################

class UnitTest:

    def __init__(self, testbank):
        self.name = testbank.attr
        self.chapters = []
        for child in testbank.children:
            self.chapters.append(Chapter(child))
        assert len(self.chapters) > 0, 'UnitTest is empty!'

class Chapter:

    def __init__(self, chapter):
        self.name = chapter.attr
        self.sections = []
        for child in chapter.children:
            self.sections.append(Section(child))
        assert len(self.sections) > 0, 'Chapter is empty!'

class Section:

    def __init__(self, section):
        self.name = section.attr
        self.categories = []
        for child in section.children:
            self.categories.append(Category(child))
        assert len(self.categories) > 0, 'Section is empty!'

################################################################################

def Category(category):
    if category.attr == 'multiple_choice':
        return Multiple_Choice(category)
    elif category.attr == 'true_or_false':
        return True_Or_False(category)
    elif category.attr == 'matching':
        return Matching(category)
    else:
        raise ValueError(category.attr)

class _Category:

    def __init__(self, category):
        self.facts = []
        for child in category.children:
            tof = self.__class__.__name__ == 'True_Or_False'
            self.facts.append(Fact(child, tof))
        assert len(self.facts) > 0, 'Category is empty!'

class _MCM(_Category):

    def build(self):
        kinds = {}
        for fact in self.facts:
            fact.build()
            if fact.kind not in kinds:
                kinds[fact.kind] = {'Q': {}, 'A': {}}
            for question in fact.Q2A:
                if question in kinds[fact.kind]['Q']:
                    kinds[fact.kind]['Q'][question].update(fact.Q2A[question])
                else:
                    kinds[fact.kind]['Q'][question] = set(fact.Q2A[question])
            for answer in fact.A2Q:
                if answer in kinds[fact.kind]['A']:
                    kinds[fact.kind]['A'][answer].update(fact.A2Q[answer])
                else:
                    kinds[fact.kind]['A'][answer] = set(fact.A2Q[answer])
        self.QnA = []
        for kind in kinds:
            questions = set(kinds[kind]['Q'])
            answers = set(kinds[kind]['A'])
            for question in questions:
                wrong = answers.difference(kinds[kind]['Q'][question])
                sample = random.sample(wrong, min(len(wrong), self.CHOICES))
                right = random.choice(tuple(kinds[kind]['Q'][question]))
                sample.append(right)
                random.shuffle(sample)
                category = self.__class__.__name__.replace('_', ' ')
                self.QnA.append((category, question, tuple(sample), right))

################################################################################

class True_Or_False(_Category):

    def build(self):
        questions = {}
        for fact in self.facts:
            fact.build()
            for question in fact.Q2A:
                if question in questions:
                    questions[question].update(fact.Q2A[question])
                else:
                    questions[question] = set(fact.Q2A[question])
        for question in questions:
            assert len(questions[question]) == 1, 'Question is invalid!'
        self.QnA = []
        for question in questions:
            sample = random.sample(('True', 'False'), 2)
            right = tuple(questions[question])[0]
            self.QnA.append(('True or False', question, tuple(sample), right))

class Multiple_Choice(_MCM): CHOICES = 3

class Matching(_MCM): CHOICES = 25

################################################################################

class Fact:

    def __init__(self, fact, tof):
        self.kind = fact.attr
        self.questions = []
        self.answers = []
        for child in fact.children:
            if isinstance(child, testbank.Question):
                self.questions.append(Question(child))
            elif isinstance(child, testbank.Answer):
                self.answers.append(Answer(child, tof))
            else:
                raise TypeError(child)
        assert len(self.answers) > 0, 'Fact is empty!'

    def build(self):
        questions = frozenset(question.text for question in self.questions)
        answers = frozenset(answer.text for answer in self.answers)
        self.Q2A = dict((question, answers) for question in questions)
        self.A2Q = dict((answer, questions) for answer in answers)

class Question:

    def __init__(self, question):
        self.text = question.text

class Answer:

    def __init__(self, answer, tof):
        if tof:
            assert answer.text in ('True', 'False'), 'Answer is invalid!'
        self.text = answer.text