Welcome, guest | Sign In | My Account | Store | Cart
####################
# 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

History