~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to www/apps/tutorial/__init__.py

  • Committer: mattgiuca
  • Date: 2008-03-15 14:19:29 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:710
Tutorial: The tutorial system now presents a table of contents at the top.
    (This is supposed to be a side bar, but the CSS stylings was too gnarly
    for me tonight!)
    The TOC presents all h1,h2,h3 and exercise nodes in a list.
    The exercises are presented with their green/grey balls to indicate
    completion.
The tutorial JavaScript automatically updates the balls in the
table-of-contents to green if they pass, dynamically.

Show diffs side-by-side

added added

removed removed

Lines of Context:
240
240
    req.write('<div id="ivle_padding">\n')
241
241
    req.write("<h1>IVLE Tutorials - %s</h1>\n<h2>%s</h2>\n"
242
242
        % (cgi.escape(subject), cgi.escape(worksheetname)))
 
243
    present_table_of_contents(req, worksheetdom, 0)
243
244
 
244
245
    # Write each element
245
246
    exerciseid = 0
247
248
        exerciseid = present_worksheet_node(req, node, exerciseid)
248
249
    req.write("</div>\n")   # tutorialbody
249
250
 
 
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.
 
255
 
 
256
    When exercise elements are encountered, the DB is queried for their
 
257
    completion status, and the ball is shown of the appropriate colour.
 
258
 
 
259
    exerciseid is the ID to use for the first exercise.
 
260
    """
 
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>
 
265
<ul>
 
266
""")
 
267
    db = common.db.DB()
 
268
    try:
 
269
        for tag, xml in find_all_nodes(req, node):
 
270
            if tag == "ex":
 
271
                # Exercise node
 
272
                # Fragment ID is an accumulating exerciseid
 
273
                # (The same algorithm is employed when presenting exercises)
 
274
                fragment_id = "exercise%d" % exerciseid
 
275
                exerciseid += 1
 
276
                exercisesrc = xml.getAttribute("src")
 
277
                # TODO: Get proper exercise title
 
278
                title = exercisesrc
 
279
                # Get the completion status of this exercise
 
280
                complete, _ = db.get_problem_status(req.user.login,
 
281
                    exercisesrc)
 
282
                req.write('  <li class="%s" id="toc_li_%s"><a href="#%s">%s'
 
283
                    '</a></li>\n'
 
284
                    % ("complete" if complete else "incomplete",
 
285
                        fragment_id, fragment_id, cgi.escape(title)))
 
286
            else:
 
287
                # Heading node
 
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)))
 
292
    finally:
 
293
        db.close()
 
294
    req.write('</ul>\n</div>\n')
 
295
 
 
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.
 
303
    """
 
304
    if node.nodeType == node.ELEMENT_NODE:
 
305
        if node.tagName == "exercise":
 
306
            yield "ex", node
 
307
        elif (node.tagName == "h1" or node.tagName == "h2"
 
308
            or node.tagName == "h3"):
 
309
            yield node.tagName, node
 
310
        else:
 
311
            # Some other element. Recurse.
 
312
            for childnode in node.childNodes:
 
313
                for yieldval in find_all_nodes(req, childnode):
 
314
                    yield yieldval
 
315
 
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()
287
353
    return s
288
354
 
 
355
def getID(element):
 
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.
 
359
    """
 
360
    id = element.getAttribute("id")
 
361
    if id is not None and id != '':
 
362
        return id
 
363
    for child in element.childNodes:
 
364
        if child.nodeType == child.ELEMENT_NODE:
 
365
            id = getID(child)
 
366
            if id is not None:
 
367
                return id
 
368
    return None
 
369
 
289
370
def getTextData(element):
290
371
    """ Get the text and cdata inside an element
291
372
    Leading and trailing whitespace are stripped
294
375
    for child in element.childNodes:
295
376
        if child.nodeType == child.CDATA_SECTION_NODE:
296
377
            data += child.data
297
 
        if child.nodeType == child.TEXT_NODE:
 
378
        elif child.nodeType == child.TEXT_NODE:
298
379
            data += child.data
 
380
        elif child.nodeType == child.ELEMENT_NODE:
 
381
            data += getTextData(child)
299
382
 
300
383
    return data.strip()
301
384