Generates Sphinx documentation, refactor into a module.
This commit is contained in:
parent
7ac08f910f
commit
66260d37bc
7 changed files with 802 additions and 1 deletions
8
moxquizz/__init__.py
Normal file
8
moxquizz/__init__.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#!/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
|
||||
293
moxquizz/quiz.py
Normal file
293
moxquizz/quiz.py
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
#!/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
|
||||
|
||||
|
||||
class Question:
|
||||
"""
|
||||
Represents one MoxQuizz 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
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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, usually provided by a
|
||||
:class:`QuestionBank` :attr:`~QuestionBank.parse` call.
|
||||
"""
|
||||
|
||||
## Valid keys:
|
||||
# ----------
|
||||
# Category? (should always be on top!)
|
||||
# Question (should always stand after Category)
|
||||
# Answer (will be matched if no regexp is provided)
|
||||
# Regexp? (use UNIX-style expressions)
|
||||
# Author? (the brain behind this question)
|
||||
# Level? [baby|easy|normal|hard|extreme] (difficulty)
|
||||
# Comment? (comment line)
|
||||
# Score? [#] (credits for answering this question)
|
||||
# Tip* (provide one or more hints)
|
||||
# TipCycle? [#] (Specify number of generated tips)
|
||||
|
||||
if 'Question' in attributes_dict.keys():
|
||||
self.question = attributes_dict['Question']
|
||||
else:
|
||||
raise Exception("Cannot instantiate Question: 'Question' attribute required.")
|
||||
|
||||
if 'Category' in attributes_dict.keys():
|
||||
self.category = attributes_dict['Category']
|
||||
|
||||
if 'Answer' in attributes_dict.keys():
|
||||
self.answer = attributes_dict['Answer']
|
||||
else:
|
||||
raise Exception("Cannot instantiate Question: 'Answer' attribute required.")
|
||||
|
||||
if 'Regexp' in attributes_dict.keys():
|
||||
self.regexp = attributes_dict['Regexp']
|
||||
|
||||
if 'Author' in attributes_dict.keys():
|
||||
self.category = attributes_dict['Author']
|
||||
|
||||
if 'Level' in attributes_dict.keys() and attributes_dict['Level'] in self.LEVELS:
|
||||
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']
|
||||
|
||||
if 'Score' in attributes_dict.keys():
|
||||
self.score = attributes_dict['Score']
|
||||
|
||||
if 'Tip' in attributes_dict.keys():
|
||||
self.tip = attributes_dict['Tip']
|
||||
|
||||
if 'Tipcycle' in attributes_dict.keys():
|
||||
self.tipcycle = attributes_dict['Tipcycle']
|
||||
|
||||
|
||||
class QuestionBank:
|
||||
"""
|
||||
Represents a MoxQuizz question bank.
|
||||
"""
|
||||
|
||||
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',
|
||||
'Author',
|
||||
'Category',
|
||||
'Comment',
|
||||
'Level',
|
||||
'Question',
|
||||
'Regexp',
|
||||
'Score',
|
||||
'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):
|
||||
"""
|
||||
Constructor, takes a MozQuizz-formatted question bank filename.
|
||||
"""
|
||||
self.filename = filename
|
||||
self.questions = self.parse(filename)
|
||||
|
||||
def parse(self, filename):
|
||||
"""
|
||||
Read a MoxQuizz-formatted question bank file. Returns a ``list`` of
|
||||
:class:`Question` objects found in the file.
|
||||
"""
|
||||
questions = list()
|
||||
|
||||
with open(filename) as f:
|
||||
key = ''
|
||||
i = 0
|
||||
|
||||
# new question
|
||||
q = dict()
|
||||
q['Tip'] = list()
|
||||
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
i += 1
|
||||
|
||||
# Ignore comments.
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
|
||||
# A blank line starts a new question.
|
||||
if line == '':
|
||||
# Store the previous question, if valid.
|
||||
if 'Question' in q.keys() and 'Answer' in q.keys():
|
||||
question = Question(q)
|
||||
questions.append(question)
|
||||
|
||||
# Start a new question.
|
||||
q = dict()
|
||||
q['Tip'] = list()
|
||||
continue
|
||||
|
||||
# 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
|
||||
# break # TODO: is it appropriate to bail on broken bank files?
|
||||
|
||||
# Ignore bad parameters.
|
||||
if key not in self.KEYS:
|
||||
print("Unexpected key '%s' in MoxQuizz questionbank '%s', line %s." % (key, self.filename, i))
|
||||
continue
|
||||
|
||||
# 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()
|
||||
|
||||
return questions
|
||||
|
||||
|
||||
# A crappy test.
|
||||
if __name__ == '__main__':
|
||||
qb = QuestionBank('../questions.doctorlard.en')
|
||||
for q in qb.questions:
|
||||
print(q.question)
|
||||
a = unicode(raw_input('A: '), 'utf8')
|
||||
#a = input('A: ') # Python 3
|
||||
if a.lower() == q.answer.lower():
|
||||
print("Correct!")
|
||||
else:
|
||||
print("Incorrect - the answer is '%s'" % q.answer)
|
||||
Loading…
Add table
Add a link
Reference in a new issue