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