5938f5505599e7f748180967bc0c3dcffe7c3ebd
[quizzer.git] / quizzer / ui / cli.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 cmd as _cmd
18 try:
19     import readline as _readline
20 except ImportError as _readline_import_error:
21     _readline = None
22
23 from . import UserInterface
24
25
26 class QuestionCommandLine (_cmd.Cmd):
27     _help = [
28         'Type help or ? to list commands.',
29         'Non-commands will be interpreted as answers.',
30         'Use a blank line to terminate multi-line answers.',
31         ]
32     intro = '\n'.join(['Welcome to the quizzer shell.'] + _help)
33     _prompt = 'quizzer? '
34
35     def __init__(self, ui):
36         super(QuestionCommandLine, self).__init__()
37         self.ui = ui
38
39     def preloop(self):
40         self.question = self.ui.get_question()
41         self._reset()
42
43     def _reset(self):
44         self.answers = []
45         self._set_ps1()
46
47     def _set_ps1(self):
48         "Pose a question and prompt"
49         self.prompt = '\n{}\n{}'.format(
50             self.question.format_prompt(), self._prompt)
51
52     def _set_ps2(self):
53         "Just prompt (without the question, e.g. for multi-line answers)"
54         self.prompt = self._prompt
55
56     def default(self, line):
57         self.answers.append(line)
58         if self.question.multiline:
59             self._set_ps2()
60         else:
61             self._answer()
62
63     def emptyline(self):
64         self._answer()
65
66     def _answer(self):
67         if self.question.multiline:
68             answer = self.answers
69         else:
70             answer = self.answers.get(0, '')
71         correct = self.ui.process_answer(question=self.question, answer=answer)
72         if correct:
73             print('correct\n')
74         else:
75             print('incorrect\n')
76         self.question = self.ui.get_question()
77         if not self.question:
78             return True  # out of questions
79         self._reset()
80
81     def do_quit(self, arg):
82         "Stop taking the quiz"
83         self._reset()
84         return True
85
86     def do_hint(self, arg):
87         "Show a hint for the current question"
88         self._reset()
89         print(self.question.format_help())
90
91
92 class CommandLineInterface (UserInterface):
93     def run(self):
94         cmd = QuestionCommandLine(ui=self)
95         cmd.cmdloop()
96         print()
97
98     def display_results(self):
99         print('results:')
100         for question in self.quiz:
101             if question.id in self.answers:
102                 self.display_result(question=question)
103                 print()
104         self.display_totals()
105
106     def display_result(self, question):
107         answers = self.answers.get(question.id, [])
108         print('question:')
109         print('  {}'.format(question.format_prompt(newline='\n  ')))
110         la = len(answers)
111         lc = len([a for a in answers if a['correct']])
112         print('answers: {}/{} ({:.2f})'.format(lc, la, float(lc)/la))
113         for answer in answers:
114             if answer['correct']:
115                 correct = 'correct'
116             else:
117                 correct = 'incorrect'
118             print('  you answered: {}'.format(answer['answer']))
119             print('     which was: {}'.format(correct))
120
121     def display_totals(self):
122         answered = self.answers.get_answered(questions=self.quiz)
123         correctly_answered = self.answers.get_correctly_answered(
124             questions=self.quiz)
125         la = len(answered)
126         lc = len(correctly_answered)
127         print('answered {} of {} questions'.format(la, len(self.quiz)))
128         print(('of the answered questions, {} ({:.2f}) were answered correctly'
129                ).format(lc, float(lc)/la))