quizzer: Add user keys to the answer database and stack
[quizzer.git] / quizzer / ui / __init__.py
1 # Copyright (C) 2013 W. Trevor King <wking@tremily.us>
2 #
3 # This file is part of quizzer.
4 #
5 # quizzer is free software: you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
8 # version.
9 #
10 # quizzer is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # quizzer.  If not, see <http://www.gnu.org/licenses/>.
16
17 import collections as _collections
18 import importlib as _importlib
19
20 from .. import answerdb as _answerdb
21
22
23 INTERFACES = ['cli', 'wsgi']
24
25
26 class UserInterface (object):
27     "Give a quiz over a generic user interface"
28     def __init__(self, quiz=None, answers=None, stack=None):
29         self.quiz = quiz
30         if answers is None:
31             answers = _answerdb.AnswerDatabase()
32         self.answers = answers
33         if stack is None:
34             stack = self.answers.get_never_correctly_answered(
35                 questions=quiz.leaf_questions())
36         self._stack = stack
37         self.stack = _collections.defaultdict(self._new_stack)
38
39     def _new_stack(self):
40         return list(self._stack)  # make a new copy for a new user
41
42     def run(self):
43         raise NotImplementedError()
44
45     def get_question(self, user=None):
46         if self.stack[user]:
47             return self.stack[user].pop(0)
48
49     def process_answer(self, question, answer, user=None, **kwargs):
50         correct,details = question.check(answer=answer, **kwargs)
51         self.answers.add(
52             question=question, answer=answer, correct=correct, user=user)
53         if not correct:
54             self.stack[user].insert(0, question)
55             for qid in reversed(question.dependencies):
56                 self.stack[user].insert(0, self.quiz.get(id=qid))
57         return (correct, details)
58
59
60 def get_ui(name):
61     """Get the UserInterface subclass from a UI submodule
62
63     >>> get_ui('cli')
64     <class 'quizzer.ui.cli.CommandLineInterface'>
65     """
66     module = _importlib.import_module('{}.{}'.format(__name__, name))
67     for name in dir(module):
68         obj = getattr(module, name)
69         try:
70             subclass = issubclass(obj, UserInterface)
71         except TypeError as e:  # obj is not a class
72             continue
73         if subclass:
74             return obj
75     raise ValueError(name)