Commit 52fd6a5b authored by Jonathan Harker's avatar Jonathan Harker

Rewrite to support basic quiz gameplay.

parent bbb90264
This diff is collapsed.
#!/usr/bin/env python
## -*- coding: utf-8 -*-
"""
A MozQuizz question library for Python.
See http://moxquizz.de/ for the original implementation in TCL.
"""
from __future__ import unicode_literals, print_function
from io import open
import re
import sys
class Question:
......@@ -10,30 +17,103 @@ class Question:
"""
category = None
"""
The question category. Arbitrary text; optional.
"""
question = None
"""
The question. Arbitrary text; required.
"""
answer = None
"""
The answer. Arbitrary text; required. Correct answers can also be covered
by the :attr:`regexp` property.
"""
regexp = None
"""
A regular expression that will generate correct answers. Optional. See
also the :attr:`answer` property.
"""
author = None
level = None
"""
The question author. Arbitrary text; optional.
"""
level = None # Default: NORMAL (constructor)
"""
The difficulty level. Value must be from the :attr:`LEVELS` tuple.
The default value is :attr:`NORMAL`.
"""
comment = None
score = 0
"""
A comment. Arbitrary text; optional.
"""
score = 1
"""
The points scored for the correct answer. Integer value; default is 1.
"""
tip = list()
"""
An ordered list of tips (hints) to display to users. Optional.
"""
tipcycle = 0
"""
Indicates which tip is to be displayed next, if any.
"""
TRIVIAL = 1
"""
A value for :attr:`level` that indicates a question of trivial difficulty.
"""
EASY = 2
"""
A value for :attr:`level` that indicates a question of easy difficulty.
"""
NORMAL = 3
"""
A value for :attr:`level` that indicates a question of average or normal
difficulty.
"""
HARD = 4
"""
A value for :attr:`level` that indicates a question of hard difficulty.
"""
EXTREME = 5
"""
A value for :attr:`level` that indicates a question of extreme difficulty
or obscurity.
"""
LEVELS = (TRIVIAL, EASY, NORMAL, HARD, EXTREME)
"""
The available :attr:`level` difficulty values, :attr:`TRIVIAL`, :attr:`EASY`,
:attr:`NORMAL`, :attr:`HARD` and :attr:`EXTREME`.
"""
def __init__(self, attributes_dict):
"""
Constructor that takes a dictionary of MoxQuizz key-value pairs. Usually
called from a :class:`QuestionBank`.
"""
# Set defaults first.
self.level = self.NORMAL
self.parse(attributes_dict)
def parse(self, attributes_dict):
"""
Populate fields from a dictionary of attributes (from a question bank).
Populate fields from a dictionary of attributes, usually provided by a
:class:`QuestionBank` :attr:`~QuestionBank.parse` call.
"""
## Valid keys:
......@@ -69,7 +149,9 @@ class Question:
self.category = attributes_dict['Author']
if 'Level' in attributes_dict.keys() and attributes_dict['Level'] in self.LEVELS:
self.level = attributes_dict['level']
self.level = attributes_dict['Level']
elif 'Level' in attributes_dict.keys() and attributes_dict['Level'] in QuestionBank.LEVEL_VALUES.keys():
self.level = QuestionBank.LEVEL_VALUES[attributes_dict['Level']]
if 'Comment' in attributes_dict.keys():
self.comment = attributes_dict['Comment']
......@@ -83,6 +165,9 @@ class Question:
if 'Tipcycle' in attributes_dict.keys():
self.tipcycle = attributes_dict['Tipcycle']
def attempt(self, answer):
return (self.answer is not None and self.answer.lower() == answer.lower()) or (
self.regexp is not None and re.search(self.regexp, answer, re.IGNORECASE) is not None)
class QuestionBank:
"""
......@@ -90,7 +175,15 @@ class QuestionBank:
"""
filename = ''
"""
The path or filename of the question bank file.
"""
questions = list()
"""
A list of :class:`Question` objects, constituting the questions in the
question bank.
"""
# Case sensitive, to remain backwards-compatible with MoxQuizz.
KEYS = ('Answer',
......@@ -104,17 +197,34 @@ class QuestionBank:
'Tip',
'Tipcycle',
)
"""
The valid attributes available in a MoxQuizz question bank file.
"""
LEVEL_VALUES = {
'trivial': Question.TRIVIAL,
'baby': Question.TRIVIAL,
'easy': Question.EASY,
'normal': Question.NORMAL,
'hard': Question.HARD,
'difficult': Question.HARD,
'extreme': Question.EXTREME
}
"""
Text labels for the :attr:`Question.level` difficulty values.
"""
def __init__(self, filename):
"""
Construct a question bank from a file.
Constructor, takes a MozQuizz-formatted question bank filename.
"""
self.filename = filename
self.questions = self.parse(filename)
def parse(self, filename):
"""
Read a Moxquizz question bank file into a list.
Read a MoxQuizz-formatted question bank file. Returns a ``list`` of
:class:`Question` objects found in the file.
"""
questions = list()
......@@ -149,6 +259,8 @@ class QuestionBank:
# Fetch the next parameter.
try:
(key, value) = line.split(':', 1)
key = key.strip()
value = value.strip()
except ValueError:
print("Unexpected weirdness in MoxQuizz questionbank '%s', line %s." % (self.filename, i))
continue
......@@ -162,6 +274,11 @@ class QuestionBank:
# Enumerate the Tips.
if key == 'Tip':
q['Tip'].append(value.strip())
elif key == 'Level':
if value not in self.LEVEL_VALUES:
print("Unexpected Level value '%s' in MoxQuizz questionbank '%s', line '%s'." % (value, self.filename, i))
else:
q['Level'] = self.LEVEL_VALUES[value]
else:
q[key] = value.strip()
......@@ -170,12 +287,14 @@ class QuestionBank:
# A crappy test.
if __name__ == '__main__':
qb = QuestionBank('questions.doctorlard.en')
qb = QuestionBank('questions/questions.doctorlard.en')
for q in qb.questions:
print(q.question)
if sys.version.startswith('2'):
a = unicode(raw_input('A: '), 'utf8')
#a = input('A: ') # Python 3
if a.lower() == q.answer.lower():
else:
a = input('A: ')
if q.attempt(a):
print("Correct!")
else:
print("Incorrect - the answer is '%s'" % q.answer)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment