247
248
exerciseid = present_worksheet_node(req, node, exerciseid)
248
249
req.write("</div>\n") # tutorialbody
251
def present_table_of_contents(req, node, exerciseid):
252
"""Given a node of a worksheet XML document, writes out a table of
253
contents to the request. This recursively searches for "excercise"
254
and heading elements to write out.
256
When exercise elements are encountered, the DB is queried for their
257
completion status, and the ball is shown of the appropriate colour.
259
exerciseid is the ID to use for the first exercise.
261
# XXX This means the DB is queried twice for each element.
262
# Consider caching these results for lookup later.
263
req.write("""<div id="tutorial-toc">
264
<h2>Worksheet Contents</h2>
269
for tag, xml in find_all_nodes(req, node):
272
# Fragment ID is an accumulating exerciseid
273
# (The same algorithm is employed when presenting exercises)
274
fragment_id = "exercise%d" % exerciseid
276
exercisesrc = xml.getAttribute("src")
277
# TODO: Get proper exercise title
279
# Get the completion status of this exercise
280
complete, _ = db.get_problem_status(req.user.login,
282
req.write(' <li class="%s" id="toc_li_%s"><a href="#%s">%s'
284
% ("complete" if complete else "incomplete",
285
fragment_id, fragment_id, cgi.escape(title)))
288
fragment_id = getID(xml)
289
title = getTextData(xml)
290
req.write(' <li><a href="#%s">%s</a></li>\n'
291
% (fragment_id, cgi.escape(title)))
294
req.write('</ul>\n</div>\n')
296
def find_all_nodes(req, node):
297
"""Generator. Searches through a node and yields all headings and
298
exercises. (Recursive).
299
When finding a heading, yields a pair ("hx", headingnode), where "hx" is
300
the element name, such as "h1", "h2", etc.
301
When finding an exercise, yields a pair ("ex", exercisenode), where
302
exercisenode is the DOM node for this exercise.
304
if node.nodeType == node.ELEMENT_NODE:
305
if node.tagName == "exercise":
307
elif (node.tagName == "h1" or node.tagName == "h2"
308
or node.tagName == "h3"):
309
yield node.tagName, node
311
# Some other element. Recurse.
312
for childnode in node.childNodes:
313
for yieldval in find_all_nodes(req, childnode):
250
316
def present_worksheet_node(req, node, exerciseid):
251
317
"""Given a node of a worksheet XML document, writes it out to the
252
318
request. This recursively searches for "exercise" elements and handles
286
352
s += child.toxml()
356
"""Get the first ID attribute found when traversing a node and its
357
children. (This is used to make fragment links to a particular element).
358
Returns None if no ID is found.
360
id = element.getAttribute("id")
361
if id is not None and id != '':
363
for child in element.childNodes:
364
if child.nodeType == child.ELEMENT_NODE:
289
370
def getTextData(element):
290
371
""" Get the text and cdata inside an element
291
372
Leading and trailing whitespace are stripped