~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-01-24 23:31:45 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:291
tutorial: Added code to handle top-level menu and subject menu (reads dir
    listing, XML files, and presents a list of links).
    Added code to handle a worksheet page. Presents the page as HTML,
    observing <problem> elements and reading in their XML files.
    Currently does not handle problems; just prints out their source files.
Added media/tutorial/tutorial.css (very minimal currently).

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
 
31
31
import os
32
32
import cgi
 
33
import urllib
 
34
import re
 
35
from xml.dom import minidom
33
36
 
34
37
from common import util
 
38
import conf
 
39
 
 
40
THIS_APP = "tutorial"
 
41
 
 
42
# Regex for valid identifiers (subject/worksheet names)
 
43
re_ident = re.compile("[0-9A-Za-z_]+")
 
44
 
 
45
class Worksheet:
 
46
    def __init__(self, id, name):
 
47
        self.id = id
 
48
        self.name = name
 
49
    def __repr__(self):
 
50
        return ("Worksheet(id=" + repr(self.id) + ", name=" + repr(self.name)
 
51
                + ")")
 
52
 
 
53
def make_tutorial_path(subject=None, worksheet=None):
 
54
    """Creates an absolute (site-relative) path to a tutorial sheet.
 
55
    Subject or worksheet can be None.
 
56
    Ensures that top-level or subject-level URLs end in a '/', because they
 
57
    are represented as directories.
 
58
    """
 
59
    if subject is None:
 
60
        return util.make_path(THIS_APP + '/')
 
61
    else:
 
62
        if worksheet is None:
 
63
            return util.make_path(os.path.join(THIS_APP, subject + '/'))
 
64
        else:
 
65
            return util.make_path(os.path.join(THIS_APP, subject, worksheet))
35
66
 
36
67
def handle(req):
37
68
    """Handler for the Tutorial application."""
38
69
 
39
70
    # Set request attributes
40
71
    req.content_type = "text/html"
41
 
    req.write_html_head_foot = True     # Have dispatch print head and foot
 
72
    req.styles = [
 
73
        "media/tutorial/tutorial.css",
 
74
    ]
 
75
    # Note: Don't print write_html_head_foot just yet
 
76
    # If we encounter errors later we do not want this
42
77
 
43
78
    path_segs = req.path.split(os.sep)
44
79
    subject = None
58
93
        handle_worksheet(req, subject, worksheet)
59
94
 
60
95
def handle_toplevel_menu(req):
 
96
    # This is represented as a directory. Redirect and add a slash if it is
 
97
    # missing.
 
98
    if req.uri[-1] != os.sep:
 
99
        req.throw_redirect(make_tutorial_path())
 
100
    req.write_html_head_foot = True
61
101
    req.write("<h1>IVLE Tutorials</h1>\n")
62
 
    req.write("<p>TODO: Top-level tutorial menu</p>\n")
 
102
    req.write("""<p>Welcome to the IVLE tutorial system.
 
103
  Please select a subject from the list below to take a tutorial problem sheet
 
104
  for that subject.</p>\n""")
 
105
    # Get list of subjects
 
106
    # TODO: Fetch from DB. For now, just get directory listing
 
107
    subjects = os.listdir(conf.subjects_base)
 
108
    subjects.sort()
 
109
    req.write("<h2>Subjects</h2>\n<ul>\n")
 
110
    for subject in subjects:
 
111
        req.write('  <li><a href="%s">%s</a></li>\n'
 
112
            % (urllib.quote(subject) + '/', cgi.escape(subject)))
 
113
    req.write("</ul>\n")
 
114
 
 
115
def is_valid_subjname(subject):
 
116
    m = re_ident.match(subject)
 
117
    return m is not None and m.end() == len(subject)
63
118
 
64
119
def handle_subject_menu(req, subject):
 
120
    # This is represented as a directory. Redirect and add a slash if it is
 
121
    # missing.
 
122
    if req.uri[-1] != os.sep:
 
123
        req.throw_redirect(make_tutorial_path(subject))
 
124
    # Subject names must be valid identifiers
 
125
    if not is_valid_subjname(subject):
 
126
        req.throw_error(req.HTTP_NOT_FOUND)
 
127
    # Parse the subject description file
 
128
    # The subject directory must have a file "subject.xml" in it,
 
129
    # or it does not exist (404 error).
 
130
    try:
 
131
        subjectfile = open(os.path.join(conf.subjects_base, subject,
 
132
            "subject.xml"))
 
133
    except:
 
134
        req.throw_error(req.HTTP_NOT_FOUND)
 
135
 
 
136
    # Read in data about the subject
 
137
    subjectdom = minidom.parse(subjectfile)
 
138
    subjectfile.close()
 
139
    # TEMP: All of this is for a temporary XML format, which will later
 
140
    # change.
 
141
    worksheetsdom = subjectdom.documentElement
 
142
    worksheets = []     # List of string IDs
 
143
    for worksheetdom in worksheetsdom.childNodes:
 
144
        if worksheetdom.nodeType == worksheetdom.ELEMENT_NODE:
 
145
            worksheet = Worksheet(worksheetdom.getAttribute("id"),
 
146
                worksheetdom.getAttribute("name"))
 
147
            worksheets.append(worksheet)
 
148
 
 
149
    # Now all the errors are out the way, we can begin writing
 
150
    req.write_html_head_foot = True
65
151
    req.write("<h1>IVLE Tutorials - %s</h1>\n" % cgi.escape(subject))
66
 
    req.write("<p>TODO: Subject-level menu</p>\n")
 
152
    req.write("<h2>Worksheets</h2>\n<ul>\n")
 
153
    for worksheet in worksheets:
 
154
        req.write('  <li><a href="%s">%s</a></li>\n'
 
155
            % (urllib.quote(worksheet.id), cgi.escape(worksheet.name)))
 
156
    req.write("</ul>\n")
67
157
 
68
158
def handle_worksheet(req, subject, worksheet):
 
159
    # Subject and worksheet names must be valid identifiers
 
160
    if not is_valid_subjname(subject) or not is_valid_subjname(worksheet):
 
161
        req.throw_error(req.HTTP_NOT_FOUND)
 
162
 
 
163
    # Read in worksheet data
 
164
    try:
 
165
        worksheetfile = open(os.path.join(conf.subjects_base, subject,
 
166
            worksheet + ".xml"))
 
167
    except:
 
168
        req.throw_error(req.HTTP_NOT_FOUND)
 
169
 
 
170
    worksheetdom = minidom.parse(worksheetfile)
 
171
    worksheetfile.close()
 
172
    # TEMP: All of this is for a temporary XML format, which will later
 
173
    # change.
 
174
    worksheetdom = worksheetdom.documentElement
 
175
    if worksheetdom.tagName != "worksheet":
 
176
        # TODO: Nicer error message, to help authors
 
177
        req.throw_error(req.HTTP_INTERNAL_SERVER_ERROR)
 
178
    worksheetname = worksheetdom.getAttribute("name")
 
179
    elements = []     # List of DOM elements
 
180
    for elem in worksheetdom.childNodes:
 
181
        if elem.nodeType == elem.ELEMENT_NODE:
 
182
            elements.append(elem)
 
183
 
 
184
    # Now all the errors are out the way, we can begin writing
 
185
    req.write_html_head_foot = True
69
186
    req.write("<h1>IVLE Tutorials - %s</h1>\n<h2>%s</h2>\n"
70
 
        % (cgi.escape(subject), cgi.escape(worksheet)))
71
 
    req.write("<p>TODO: Worksheet content</p>\n")
72
 
 
 
187
        % (cgi.escape(subject), cgi.escape(worksheetname)))
 
188
    # Write each element
 
189
    for elem in elements:
 
190
        if elem.tagName == "problem":
 
191
            present_problem(req, subject, elem.getAttribute("src"))
 
192
        else:
 
193
            # Just treat this as a normal HTML element
 
194
            req.write(elem.toxml() + '\n')
 
195
 
 
196
def present_problem(req, subject, problemsrc):
 
197
    """Open a problem file, and write out the problem to the request in HTML.
 
198
    subject: Subject name.
 
199
    problemfile: "src" of the problem file. A path relative to the subject
 
200
        directory.
 
201
    """
 
202
    req.write('<div class="tuteproblem">\n')
 
203
    # First normalise the path
 
204
    problemsrc = os.path.normpath(problemsrc)
 
205
    # Now if it begins with ".." or separator, then it's illegal
 
206
    if problemsrc.startswith("..") or problemsrc.startswith(os.sep):
 
207
        problemfile = None
 
208
    else:
 
209
        problemfile = os.path.join(conf.subjects_base, subject,
 
210
            problemsrc)
 
211
 
 
212
    try:
 
213
        problemfile = open(problemfile)
 
214
    except (TypeError, IOError):    # TypeError if problemfile == None
 
215
        req.write("<p><b>Server Error</b>: "
 
216
            + "Problem file could not be opened.</p>\n")
 
217
        req.write("</div>\n")
 
218
        return
 
219
    
 
220
    # TODO: Read problem file and present the problem
 
221
    # TEMP: Print out the problem XML source
 
222
    req.write("<p><b>Problem:</b></p>\n")
 
223
    req.write("<pre>%s</pre>\n" % cgi.escape(problemfile.read()))
 
224
    req.write("</div>\n")