summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/webquiz/webquiz_xml.py
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/scripts/webquiz/webquiz_xml.py')
-rwxr-xr-xMaster/texmf-dist/scripts/webquiz/webquiz_xml.py386
1 files changed, 386 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/webquiz/webquiz_xml.py b/Master/texmf-dist/scripts/webquiz/webquiz_xml.py
new file mode 100755
index 00000000000..b1db71a2dab
--- /dev/null
+++ b/Master/texmf-dist/scripts/webquiz/webquiz_xml.py
@@ -0,0 +1,386 @@
+r'''
+-----------------------------------------------------------------------------
+ webquiz_xml | xml reader for reading the xml file generated by tex4ht
+-----------------------------------------------------------------------------
+
+ Copyright (C) Andrew Mathas and Donald Taylor, University of Sydney
+
+ Distributed under the terms of the GNU General Public License (GPL)
+ http://www.gnu.org/licenses/
+
+ This file is part of the Math_quiz system.
+
+ <Andrew.Mathas@sydney.edu.au>
+ <Donald.Taylor@sydney.edu.au>
+-----------------------------------------------------------------------------
+'''
+
+# -*- encoding: utf-8 -*-
+
+import xml.sax
+
+# imports of webquiz code
+import webquiz_util
+
+# ---------------------------------------------------------------------------------------
+def ReadWebQuizXmlFile(quizfile, defaults):
+ r'''
+ Set up, call and then return the xml parser
+ for the quiz web page
+ '''
+ parser = xml.sax.make_parser()
+ quiz = QuizHandler(defaults)
+ parser.setContentHandler(quiz)
+ parser.setErrorHandler(quiz)
+ parser.setDTDHandler(quiz) # as far as I can see this does nothing...
+ parser.parse(quizfile)
+ parser.close()
+ return quiz
+
+
+# ---------------------------------------------------------------------------------------
+class Data(object):
+ r'''
+ A wrapper object class that holds the data for the different
+ components of the quiz.
+ '''
+ def __init__(self, **args):
+ '''
+ Accepts key-value pairs, each of which is stored as an attribute
+ '''
+ self._items = args.items()
+ for key, val in args.items():
+ setattr(self, key, val)
+
+
+class QuizHandler(xml.sax.ContentHandler):
+ """
+ The content handler gives the xml tags to `startElement`, which
+ initialises the webquiz tags, and then `endElement` attaches the
+ content of each webquiz tag tothe appropriate part of `self`. Any end
+ tag that is ot special to webquiz has its contents appended to
+ `self.text`. Any tag that contains `DeFaUlT` is set to the system
+ default using the `defaults` dictionary.
+ """
+
+ def __init__(self, defaults):
+ self.defaults = defaults
+
+ # arrays for the different quiz components
+ self.discussion_list = []
+ self.question_list = []
+ self.quiz_index = []
+
+ # these will contain the link and meta elements from the xml file but they
+ # are ignored by webquiz.py
+ self.link_list = []
+ self.meta_list = []
+
+ # to add mathjs when an eval comparison is used
+ self.mathjs = False
+
+ # the following tags have defaults set by `defaults`
+ self.setting_tags = [
+ 'department',
+ 'department_url',
+ 'institution',
+ 'institution_url',
+ 'language',
+ 'theme',
+ ]
+ # quiz data
+ for tag in self.setting_tags:
+ setattr(self, tag, defaults[tag])
+ self.breadcrumb = ''
+ self.text = ''
+ self.after_text = ''
+ self.title = ''
+ self.unit_code = ''
+ self.unit_name = ''
+
+ # keep track of current tags for debugging...
+ self.current_tags=[]
+
+ def webquiz_debug(self, msg):
+ r'''
+ Customised debugging message for the xml module
+ '''
+ webquiz_util.webquiz_debug(self.defaults.debugging, 'xml: '+msg)
+
+ def webquiz_error(self, msg, err=None):
+ r'''
+ Customised error message for the xml module
+ '''
+ webquiz_util.webquiz_error(self.defaults.debugging, 'xml: '+msg, err)
+
+ def set_default_attribute(self, key, value):
+ ''' Set the attribute `key` of self, using the default value if
+ `value=='DeFaUlT'`.
+ '''
+ if value.strip() == 'DeFaUlT':
+ setattr(self, key, self.defaults[key])
+ else:
+ setattr(self, key, value)
+ self.webquiz_debug('Just set "{}" equal to "{}" from "{}"'.format(key, getattr(self, key), value))
+
+ #---- start of start elements --------------------------------------------
+ def startElement(self, tag, attributes):
+ '''
+ At the start of each webquiz xml tag we need to pull out the
+ attributes and place
+ '''
+ self.webquiz_debug('Starting tag for '+tag)
+ self.current_tags.append(tag)
+
+ if hasattr(self, 'start_'+tag):
+ getattr(self, 'start_'+tag)(attributes)
+
+ elif tag in ['department', 'institution', 'uni']:
+ for key in attributes.keys():
+ self.set_default_attribute(tag, attributes.get(key))
+
+ def start_webquiz(self, attributes):
+ r'''
+ Start element for tag="webquiz". Initialise the quix and extract
+ and process the list of attributes.
+ '''
+ for key in attributes.keys():
+ self.set_default_attribute(key, attributes.get(key))
+
+ # convert the following attibutes to booleans
+ for key in ['debugging', 'hide_side_menu', 'one_page', 'pst2pdf', 'random_order']:
+ setattr(self, key, getattr(self, key)=='true')
+
+ setattr(self, 'language', self.language.lower())
+ setattr(self, 'theme', self.theme.lower())
+
+ # set debugging mode from the latex file...from this point on
+ self.defaults.debugging = self.defaults.debugging or self.debugging
+
+ def start_link(self, attributes):
+ r'''
+ Start element for tag="link"
+ '''
+ self.link_list.append({key: attributes.get(key) for key in attributes.keys()})
+
+ def start_meta(self, attributes):
+ r'''
+ Start element for tag="meta"
+ '''
+ self.meta_list.append({key: attributes.get(key) for key in attributes.keys()})
+
+ def start_breadcrumb(self, attributes):
+ r'''
+ Start element for tag="breadcrumb"
+ '''
+ self.set_default_attribute('breadcrumbs', attributes.get('breadcrumbs'))
+
+ def start_unit_name(self, attributes):
+ r'''
+ Start element for tag="unit_name"
+ '''
+ self.set_default_attribute('unit_url', attributes.get('url'))
+ self.quizzes_url = attributes.get('quizzes_url')
+ if self.quizzes_url == 'DeFaUlT':
+ self.quizzes_url = self.unit_url + '/Quizzes'
+
+ def start_discussion(self, attributes):
+ r'''
+ Start element for tag="discussion"
+ '''
+ discussion = Data(heading = '',
+ short_heading = '',
+ text = '' # The text of the discussion
+ )
+ self.discussion_list.append(discussion)
+
+ def start_question(self, attributes):
+ r'''
+ Start element for tag="question"
+ '''
+ self.question_list.append(
+ Data(text = '', # The text of the question
+ type = None, # input, or single or multiple choice
+ after_text = '' # text at end of question
+ )
+ )
+
+ def start_answer(self, attributes):
+ r'''
+ Process the different question types, items choice and feedback
+ '''
+ if self.question_list[-1].type != None:
+ self.webquiz_error('question {} has too many question types: {} and input'.format(
+ len(self.question_list)+1, self.question_list[-1].type)
+ )
+ self.question_list[-1].type = 'input'
+ self.question_list[-1].answer = ''
+ self.question_list[-1].feedback_right = ''
+ self.question_list[-1].feedback_wrong = ''
+ self.question_list[-1].text += self.text
+ self.text = ''
+
+ self.question_list[-1].comparison = attributes.get('comparison')
+ self.question_list[-1].prompt = attributes.get('prompt')=='true'
+ if self.question_list[-1].comparison in ['complex', 'number']:
+ self.mathjs = True
+
+ def start_choice(self, attributes):
+ r'''
+ Start element for tag="choice"
+ '''
+ if self.question_list[-1].type != None:
+ self.webquiz_error('question {} has too many question types: {} and choice'.format(
+ len(self.question_list)+1, self.question_list[-1].type)
+ )
+ self.question_list[-1].type = attributes.get('type')
+ self.question_list[-1].columns = int(attributes.get('columns'))
+ self.question_list[-1].items = []
+ self.question_list[-1].correct = 0
+ self.question_list[-1].text += self.text
+ self.text = ''
+
+ def start_item(self, attributes):
+ r'''
+ Start element for tag="item"
+ '''
+ self.question_list[-1].items.append(
+ Data(correct= attributes.get('correct'),
+ symbol=attributes.get('symbol'),
+ feedback='',
+ text=''
+ )
+ )
+ if attributes.get('correct')=='true':
+ self.question_list[-1].correct += 1
+
+ def start_index_item(self, attributes):
+ r'''
+ Finally look after the index file
+ '''
+ self.quiz_index.append(Data(
+ prompt=attributes.get('prompt')=='true',
+ url=attributes.get('url'),
+ title=''
+ )
+ )
+
+ def start_when(self, attributes):
+ r'''
+ start element for tag="when"
+ '''
+ if self.text.strip() != '':
+ self.question_list[-1].after_text += ' '+self.text.strip()
+ self.webquiz_debug('After_text is now {}'.format(self.question_list[-1].after_text))
+ self.text = ''
+ self.current_tags[-1] = 'feedback_'+attributes.get('type')
+
+ #---- end of start elements ---------------------------------------------
+
+ def endElement(self, tag):
+ self.webquiz_debug('ending tag for {} (should be {})'.format(tag, self.current_tags[-1]))
+
+ reset_text = True
+ if hasattr(self, 'end_'+tag):
+ getattr(self, 'end_'+tag)()
+ self.text = ''
+
+ elif tag in self.setting_tags:
+ self.set_default_attribute(tag, self.text)
+ self.text = ''
+
+ elif tag in ['heading', 'short_heading']:
+ setattr(self.discussion_list[-1], tag, self.text.strip())
+
+ elif tag in ['breadcrumb', 'title', 'unit_code', 'unit_name']:
+ setattr(self, tag, self.text.strip())
+
+ else:
+ # self.text lives to be used another day
+ reset_text = False
+
+ if reset_text:
+ self.text = ''
+
+ # remove the last tag from the tag list
+ self.current_tags.pop()
+
+ #---- start of the end elements ------------------------------------------
+
+ def end_answer(self):
+ r'''
+ Process end tag when tag="answer"
+ '''
+ self.question_list[-1].answer = self.text.strip()
+
+ def end_discussion(self):
+ r'''
+ Process end tag when tag="discussion"
+ '''
+ self.discussion_list[-1].text = self.text.strip()
+
+ def end_item(self):
+ r'''
+ Process end tag when tag="item"
+ '''
+ self.question_list[-1].items[-1].text = self.text.strip()
+
+ def end_feedback(self):
+ r'''
+ Process end tag when tag="feedback"
+ '''
+ self.question_list[-1].items[-1].feedback = self.text.strip()
+
+ def end_question(self):
+ r'''
+ Process end tag when tag="question"
+ '''
+ # first some error checking
+ if self.question_list[-1].type == None:
+ self.webquiz_error('Question {} does not have an \\answer or choice environment'.format(
+ len(self.question_list)+1))
+
+ elif hasattr(self.question_list[-1], 'items'):
+ if len(self.question_list[-1].items)==0:
+ self.webquiz_error('question {} has no multiple choice items'.format(
+ len(self.question_list)+1))
+
+ if self.question_list[-1].type=='single' and self.question_list[-1].correct!=1:
+ self.webquiz_error('question {} is single-choice but has {} correct answers'.format(
+ len(self.question_list)+1,
+ self.question_list[-1].correct
+ )
+ )
+ elif not hasattr(self.question_list[-1], 'answer') or self.question_list[-1].answer=='':
+ self.webquiz_error('question {} does have not an \answer or multiple choice'.format(
+ len(self.question_list)+1))
+
+ if self.text.strip() != '':
+ self.question_list[-1].after_text += ' '+self.text.strip()
+
+ def end_index_item(self):
+ r'''
+ Process end tag when tag="index_item"
+ '''
+ self.quiz_index[-1].title = self.text.strip().replace('\n',' ').replace('\r',' ')
+
+ def end_when(self):
+ r'''
+ Process end tag when tag="index_item"
+ '''
+ self.webquiz_debug('WHEN: Adding text to '+self.current_tags[-1])
+ setattr(self.question_list[-1], self.current_tags[-1], self.text.strip())
+
+ #---- end of end elements -----------------------------------------------
+
+ def characters(self, text): #data,start,length):
+ r'''
+ Append everything to `self.text`
+ '''
+ self.text += text
+
+ def error(self, e):
+ self.webquiz_error('unknown error', e)
+
+ def fatalError(self, e):
+ self.webquiz_error('unknown fatal error', e)