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.
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
|