question: Move ChoiceQuestion.open_ended to Question.accept_all
authorW. Trevor King <wking@tremily.us>
Thu, 14 Mar 2013 12:13:08 +0000 (08:13 -0400)
committerW. Trevor King <wking@tremily.us>
Thu, 14 Mar 2013 12:18:06 +0000 (08:18 -0400)
Accepting all answers is a more general case of an open ended multiple
choice question.  It also allows us to ask questions that are
difficult or impossible to grade automatically.  We just record the
student's answer for the grader to look at later.

quizzer/question.py
quizzer/ui/cli.py
quizzer/ui/wsgi.py

index cc4cfff34a43c3be2fb840a7d2047db77f477e99..95d902284e56032bb5ad98f3bb8e67d4b690dc2e 100644 (file)
@@ -34,6 +34,7 @@ class Question (object):
         'id',
         'prompt',
         'answer',
+        'accept_all',
         'multiline',
         'help',
         'dependencies',
@@ -57,20 +58,26 @@ class Question (object):
     def __setstate__(self, state):
         if 'id' not in state:
             state['id'] = state.get('prompt', None)
-        if 'multiline' not in state:
-            state['multiline'] = False
         if 'dependencies' not in state:
             state['dependencies'] = []
         if 'tags' not in state:
             state['tags'] = set()
         else:
             state['tags'] = set(state['tags'])
+        for attr in ['accept_all', 'multiline']:
+            if attr not in state:
+                state[attr] = False
         for attr in self._state_attributes:
             if attr not in state:
                 state[attr] = None
         self.__dict__.update(state)
 
     def check(self, answer):
+        if self.accept_all:
+            return (True, None)
+        return self._check(answer)
+
+    def _check(self, answer):
         correct = answer == self.answer
         details = None
         if not correct:
@@ -95,7 +102,7 @@ class NormalizedStringQuestion (Question):
     def normalize(self, string):
         return string.strip().lower()
 
-    def check(self, answer):
+    def _check(self, answer):
         normalized_answer = self.normalize(answer)
         correct = normalized_answer == self.normalize(self.answer)
         details = None
@@ -108,17 +115,16 @@ class NormalizedStringQuestion (Question):
 class ChoiceQuestion (Question):
     _state_attributes = Question._state_attributes + [
         'display_choices',
-        'open_ended',
         ]
 
     def __setstate__(self, state):
-        for key in ['display_choices', 'open_ended']:
+        for key in ['display_choices']:
             if key not in state:
                 state[key] = False
         super(ChoiceQuestion, self).__setstate__(state)
 
-    def check(self, answer):
-        correct = answer in self.answer or self.open_ended
+    def _check(self, answer):
+        correct = answer in self.answer
         details = None
         if not correct:
             details = 'answer ({}) is not in list of expected values'.format(
@@ -229,7 +235,7 @@ class ScriptQuestion (Question):
         return (a_status,a_stdout,a_stderr,
                 t_status,t_stdout,t_stderr)
 
-    def check(self, answer=None, tempdir=None):
+    def _check(self, answer=None, tempdir=None):
         """Compare the user's answer with expected values
 
         Arguments are passed through to ._invoke() for calculating the
index e91a0dd25060e475d015c76677d63c50bd4ee332..cbfa16df3be4cef7a8e968161e191ce66361c642 100644 (file)
@@ -90,7 +90,7 @@ class QuestionCommandLine (_cmd.Cmd):
                 self.question.display_choices):
             for i,choice in enumerate(self.question.answer):
                 yield '{}) {}'.format(i, choice)
-            if self.question.open_ended:
+            if self.question.accept_all:
                 yield 'or fill in something else'
         return []
 
index de2c595372cc2b344e68134cf15b47eda2d19463..91f43210f8d190ffe8fe0e47207852f3bfeec542 100644 (file)
@@ -248,9 +248,9 @@ class QuestionApp (WSGI_DataObject):
                 ('<input type="radio" name="answer" value="{0}"/>{0}<br/>'
                  ).format(answer)
                 for answer in question.answer]
-            if question.open_ended:
+            if question.accept_all:
                 choices.extend([
-                    '<input type="radio" name="answer" value="open_ended"/>',
+                    '<input type="radio" name="answer" value="answer-other"/>',
                     '<input type="text" size="60" name="answer-other"/>'])
             answer_element = '\n'.join(choices)
         elif question.multiline:
@@ -300,8 +300,8 @@ class QuestionApp (WSGI_DataObject):
             raise HandlerError(404, 'Not Found') from e
         if (isinstance(question, _question.ChoiceQuestion) and
                 question.display_choices and
-                question.open_ended and
-                raw_answer == 'open_ended'):
+                question.accept_all and
+                raw_answer == 'answer-other'):
             answer = print_answer = data.get('answer-other', None)
         elif question.multiline:
             answer = raw_answer.splitlines()