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

193 by mattgiuca
Apps: Added stubs for the 3 new apps, Editor, Console and Tutorial.
1
# IVLE
2
# Copyright (C) 2007-2008 The University of Melbourne
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
18
# Author: Matt Giuca, Will Grant
19
20
'''Tutorial/worksheet/exercise application.
21
22
Displays tutorial content with editable exercises, allowing students to test
23
and submit their solutions to exercises and have them auto-tested.
24
'''
287 by mattgiuca
setup.py: Added new conf.py variable: subjects_base. This is for storing the
25
26
import os
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
27
import urllib
28
import re
1099.1.36 by William Grant
ivle.webapp.tutorial: Clean up.
29
import mimetypes
30
from datetime import datetime
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
31
from xml.dom import minidom
193 by mattgiuca
Apps: Added stubs for the 3 new apps, Editor, Console and Tutorial.
32
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
33
import genshi
307 by mattgiuca
tutorial: Now each problem div has an ID. Added submit buttons which call
34
1080.1.32 by me at id
www/app/{subjects,tutorial}: Use the new Storm API to get enrolled subjects.
35
import ivle.database
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
36
from ivle.database import Subject, Offering, Semester, Exercise, \
1294.2.64 by William Grant
Port tutorial stuff.
37
                          ExerciseSave, WorksheetExercise, ExerciseAttempt
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
38
from ivle.database import Worksheet as DBWorksheet
1099.1.220 by Nick Chadwick
Merged from trunk
39
import ivle.worksheet.utils
1294.2.64 by William Grant
Port tutorial stuff.
40
from ivle.webapp import ApplicationRoot
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
41
from ivle.webapp.base.views import BaseView
42
from ivle.webapp.base.xhtml import XHTMLView
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
43
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
1294.2.64 by William Grant
Port tutorial stuff.
44
from ivle.webapp.media import media_url#, BaseMediaFileView
1294.2.71 by William Grant
Move i.w.tutorial traversal stuff into i.w.tutorial.traversal.
45
from ivle.webapp.errors import NotFound
1099.1.220 by Nick Chadwick
Merged from trunk
46
from ivle.worksheet.rst import rst as rstfunc
1294.2.71 by William Grant
Move i.w.tutorial traversal stuff into i.w.tutorial.traversal.
47
1294.2.64 by William Grant
Port tutorial stuff.
48
from ivle.webapp.tutorial.service import (AttemptsRESTView, AttemptRESTView,
1294.2.71 by William Grant
Move i.w.tutorial traversal stuff into i.w.tutorial.traversal.
49
            WorksheetExerciseRESTView, WorksheetRESTView, WorksheetsRESTView)
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
50
from ivle.webapp.tutorial.exercise_service import ExercisesRESTView, \
51
                                                  ExerciseRESTView
1294.3.2 by William Grant
Router->Publisher
52
from ivle.webapp.tutorial.publishing import (root_to_exercise, exercise_url,
1294.2.71 by William Grant
Move i.w.tutorial traversal stuff into i.w.tutorial.traversal.
53
            offering_to_worksheet, worksheet_url,
54
            worksheet_to_worksheetexercise, worksheetexercise_url,
55
            ExerciseAttempts, worksheetexercise_to_exerciseattempts,
56
            exerciseattempts_url, exerciseattempts_to_attempt,
57
            exerciseattempt_url)
1294.2.100 by William Grant
Add exercise/worksheet breadcrumbs.
58
from ivle.webapp.tutorial.breadcrumbs import (ExerciseBreadcrumb,
59
            WorksheetBreadcrumb)
523 by stevenbird
Adding ReStructured Text preprocessing of exercise descriptions,
60
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
61
class Worksheet:
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
62
    """This class represents a worksheet and a particular students progress
63
    through it.
64
    
65
    Do not confuse this with a worksheet in the database. This worksheet
66
    has extra information for use in the output, such as marks."""
734 by mattgiuca
tutorial: BEHAVIOUR CHANGE
67
    def __init__(self, id, name, assessable):
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
68
        self.id = id
69
        self.name = name
734 by mattgiuca
tutorial: BEHAVIOUR CHANGE
70
        self.assessable = assessable
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
71
        self.complete_class = ''
72
        self.optional_message = ''
73
        self.total = 0
74
        self.mand_done = 0
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
75
    def __repr__(self):
734 by mattgiuca
tutorial: BEHAVIOUR CHANGE
76
        return ("Worksheet(id=%s, name=%s, assessable=%s)"
77
                % (repr(self.id), repr(self.name), repr(self.assessable)))
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
78
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
79
class OfferingView(XHTMLView):
80
    '''The view of the index of worksheets for an offering.'''
1099.1.192 by Nick Chadwick
Moved the tutorial templates in a new directory to keep tutorial cleaner
81
    template = 'templates/subjectmenu.html'
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
82
    tab = 'subjects' # XXX
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
83
    permission = 'view'
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
84
85
    def populate(self, req, ctx):
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
86
        """Create the context for the given offering."""
1099.1.64 by William Grant
Move ivle.webapp.tutorial's media to the new framework. This also fixes the
87
        self.plugin_styles[Plugin] = ['tutorial.css']
88
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
89
        ctx['subject'] = self.context.subject
1140 by William Grant
Show a link to WorksheetsEditView on WorksheetsView if we have privileges.
90
        ctx['offering'] = self.context
91
        ctx['user'] = req.user
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
92
93
        # As we go, calculate the total score for this subject
94
        # (Assessable worksheets only, mandatory problems only)
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
95
96
        ctx['worksheets'] = []
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
97
        problems_done = 0
98
        problems_total = 0
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
99
        # Offering.worksheets is ordered by the worksheets seq_no
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
100
        for worksheet in self.context.worksheets:
101
            new_worksheet = Worksheet(worksheet.identifier, worksheet.name, 
102
                                      worksheet.assessable)
103
            if new_worksheet.assessable:
104
                # Calculate the user's score for this worksheet
105
                mand_done, mand_total, opt_done, opt_total = (
1099.1.220 by Nick Chadwick
Merged from trunk
106
                    ivle.worksheet.utils.calculate_score(req.store, req.user,
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
107
                        worksheet))
108
                if opt_total > 0:
109
                    optional_message = " (excluding optional exercises)"
110
                else:
111
                    optional_message = ""
112
                if mand_done >= mand_total:
113
                    new_worksheet.complete_class = "complete"
114
                elif mand_done > 0:
115
                    new_worksheet.complete_class = "semicomplete"
116
                else:
117
                    new_worksheet.complete_class = "incomplete"
118
                problems_done += mand_done
119
                problems_total += mand_total
120
                new_worksheet.mand_done = mand_done
121
                new_worksheet.total = mand_total
122
                new_worksheet.optional_message = optional_message
123
            ctx['worksheets'].append(new_worksheet)
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
124
125
        ctx['problems_total'] = problems_total
126
        ctx['problems_done'] = problems_done
127
        if problems_total > 0:
128
            if problems_done >= problems_total:
129
                ctx['complete_class'] = "complete"
130
            elif problems_done > 0:
131
                ctx['complete_class'] = "semicomplete"
132
            else:
133
                ctx['complete_class'] = "incomplete"
1195.1.13 by Matt Giuca
ivle.worksheet.utils: Added calculate_mark, which is from the duplicated code
134
            # Calculate the final percentage and mark for the subject
135
            ctx['problems_pct'], ctx['mark'], ctx['max_mark'] = (
136
                ivle.worksheet.utils.calculate_mark(
137
                    problems_done, problems_total))
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
138
139
class WorksheetView(XHTMLView):
140
    '''The view of a worksheet with exercises.'''
1099.1.192 by Nick Chadwick
Moved the tutorial templates in a new directory to keep tutorial cleaner
141
    template = 'templates/worksheet.html'
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
142
    tab = 'subjects'
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
143
    permission = 'view'
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
144
145
    def populate(self, req, ctx):
1099.1.64 by William Grant
Move ivle.webapp.tutorial's media to the new framework. This also fixes the
146
        self.plugin_scripts[Plugin] = ['tutorial.js']
1099.1.218 by Nick Chadwick
tutorials can now use RST
147
        self.plugin_styles[Plugin] = ['tutorial.css', 'worksheet.css']
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
148
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
149
        if not self.context:
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
150
            raise NotFound()
151
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
152
        ctx['subject'] = self.context.offering.subject
153
        ctx['worksheet'] = self.context
1294.2.64 by William Grant
Port tutorial stuff.
154
        ctx['semester'] = self.context.offering.semester.semester
155
        ctx['year'] = self.context.offering.semester.year
1099.1.220 by Nick Chadwick
Merged from trunk
156
157
        ctx['worksheetstream'] = genshi.Stream(list(genshi.XML(self.context.get_xml())))
1141 by William Grant
Display an edit link in WorksheetView, if we have privileges.
158
        ctx['user'] = req.user
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
159
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
160
        generate_worksheet_data(ctx, req, self.context)
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
161
162
        ctx['worksheetstream'] = add_exercises(ctx['worksheetstream'], ctx, req)
163
1294.2.20 by William Grant
Drop SubjectMediaView for now, while stuff is ported.
164
#class SubjectMediaView(BaseMediaFileView):
165
#    '''The view of subject media files.
166
#
167
#    URIs pointing here will just be served directly, from the subject's
168
#    media directory.
169
#    '''
170
#    permission = 'view'
171
#
172
#    def __init__(self, req, subject, path):
173
#        self.context = req.store.find(Subject, short_name=subject).one()
174
#        self.path = os.path.normpath(path)
175
#
176
#    def _make_filename(self, req):
177
#        # If the subject doesn't exist, self.subject will be None. Die.
178
#        if not self.context:
179
#            raise NotFound()
180
#
181
#        subjectdir = os.path.join(req.config['paths']['data'],
182
#                                  'content/subjects',
183
#                                  self.context.short_name, 'media')
184
#        return os.path.join(subjectdir, self.path)
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
185
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
186
def get_worksheets(subjectfile):
187
    '''Given a subject stream, get all the worksheets and put them in ctx'''
188
    worksheets = []
189
    for kind, data, pos in subjectfile:
190
        if kind is genshi.core.START:
191
            if data[0] == 'worksheet':
192
                worksheetid = ''
193
                worksheetname = ''
194
                worksheetasses = False
195
                for attr in data[1]:
196
                    if attr[0] == 'id':
197
                        worksheetid = attr[1]
198
                    elif attr[0] == 'name':
199
                        worksheetname = attr[1]
200
                    elif attr[0] == 'assessable':
201
                        worksheetasses = attr[1] == 'true'
202
                worksheets.append(Worksheet(worksheetid, worksheetname, \
203
                                                            worksheetasses))
204
    return worksheets
205
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
206
# This generator adds in the exercises as they are required. This is returned.
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
207
def add_exercises(stream, ctx, req):
1099.1.42 by Nick Chadwick
Fixed an oversight in the tutorial code which was printing <worksheet>
208
    """A filter which adds exercises into the stream."""
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
209
    exid = 0
210
    for kind, data, pos in stream:
211
        if kind is genshi.core.START:
1099.1.42 by Nick Chadwick
Fixed an oversight in the tutorial code which was printing <worksheet>
212
            # Remove the worksheet tags, as they are not xhtml valid.
213
            if data[0] == 'worksheet':
214
                continue
215
            # If we have an exercise node, replace it with the content of the
216
            # exercise.
217
            elif data[0] == 'exercise':
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
218
                # XXX: Note that we presume ctx['exercises'] has a correct list
219
                #      of exercises. If it doesn't, something has gone wrong.
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
220
                new_stream = ctx['exercises'][exid]['stream']
221
                exid += 1
222
                for item in new_stream:
223
                    yield item
224
            else:
225
                yield kind, data, pos
1099.1.42 by Nick Chadwick
Fixed an oversight in the tutorial code which was printing <worksheet>
226
        # Remove the end tags for exercises and worksheets
227
        elif kind is genshi.core.END:
228
            if data == 'exercise':
229
                continue
230
            elif data == 'worksheet':
231
                continue
232
            else:
233
                yield kind, data, pos
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
234
        else:
235
            yield kind, data, pos
236
237
# This function runs through the worksheet, to get data on the exercises to
238
# build a Table of Contents, as well as fill in details in ctx
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
239
def generate_worksheet_data(ctx, req, worksheet):
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
240
    """Runs through the worksheetstream, generating the exericises"""
241
    ctx['exercises'] = []
242
    ctx['exerciselist'] = []
243
    for kind, data, pos in ctx['worksheetstream']:
244
        if kind is genshi.core.START:
245
            if data[0] == 'exercise':
246
                src = ""
247
                optional = False
248
                for attr in data[1]:
249
                    if attr[0] == 'src':
250
                        src = attr[1]
251
                    if attr[0] == 'optional':
252
                        optional = attr[1] == 'true'
253
                # Each item in toc is of type (name, complete, stream)
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
254
                if src != "":
255
                    ctx['exercises'].append(present_exercise(req, src, worksheet))
256
                    ctx['exerciselist'].append((src, optional))
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
257
            elif data[0] == 'worksheet':
258
                ctx['worksheetname'] = 'bob'
259
                for attr in data[1]:
260
                    if attr[0] == 'name':
261
                        ctx['worksheetname'] = attr[1]
425 by mattgiuca
tutorial: Refactored present_worksheet so it has a separate function for
262
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
263
def innerXML(elem):
264
    """Given an element, returns its children as XML strings concatenated
265
    together."""
266
    s = ""
267
    for child in elem.childNodes:
268
        s += child.toxml()
269
    return s
270
271
def getTextData(element):
272
    """ Get the text and cdata inside an element
273
    Leading and trailing whitespace are stripped
274
    """
275
    data = ''
276
    for child in element.childNodes:
277
        if child.nodeType == child.CDATA_SECTION_NODE:
278
            data += child.data
710 by mattgiuca
Tutorial: The tutorial system now presents a table of contents at the top.
279
        elif child.nodeType == child.TEXT_NODE:
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
280
            data += child.data
710 by mattgiuca
Tutorial: The tutorial system now presents a table of contents at the top.
281
        elif child.nodeType == child.ELEMENT_NODE:
282
            data += getTextData(child)
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
283
284
    return data.strip()
285
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
286
def present_exercise(req, src, worksheet):
515 by stevenbird
Propagated "problem" -> "exercise" nomenclature change.
287
    """Open a exercise file, and write out the exercise to the request in HTML.
288
    exercisesrc: "src" of the exercise file. A path relative to the top-level
289
        exercises base directory, as configured in conf.
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
290
    """
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
291
    # Exercise-specific context is used here, as we already have all the data
292
    # we need
293
    curctx = genshi.template.Context()
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
294
295
    worksheet_exercise = req.store.find(WorksheetExercise,
296
        WorksheetExercise.worksheet_id == worksheet.id,
297
        WorksheetExercise.exercise_id == src).one()
298
299
    if worksheet_exercise is None:
300
        raise NotFound()
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
301
302
    # Retrieve the exercise details from the database
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
303
    exercise = req.store.find(Exercise, 
304
        Exercise.id == worksheet_exercise.exercise_id).one()
305
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
306
    if exercise is None:
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
307
        raise NotFound(exercisesrc)
1099.1.93 by William Grant
Remove remaining uses of req.throw_error in the new webapps.
308
515 by stevenbird
Propagated "problem" -> "exercise" nomenclature change.
309
    # Read exercise file and present the exercise
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
310
    # Note: We do not use the testing framework because it does a lot more
515 by stevenbird
Propagated "problem" -> "exercise" nomenclature change.
311
    # work than we need. We just need to get the exercise name and a few other
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
312
    # fields from the XML.
313
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
314
    curctx['exercise'] = exercise
315
    if exercise.description is not None:
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
316
        desc = rstfunc(exercise.description)
317
        curctx['description'] = genshi.XML('<div id="description">' + desc + 
318
                                           '</div>')
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
319
    else:
320
        curctx['description'] = None
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
321
702 by mattgiuca
tutorial:
322
    # If the user has already saved some text for this problem, or submitted
323
    # an attempt, then use that text instead of the supplied "partial".
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
324
    # Get exercise stored text will return a save, or the most recent attempt,
325
    # whichever is more recent
1099.1.220 by Nick Chadwick
Merged from trunk
326
    save = ivle.worksheet.utils.get_exercise_stored_text(
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
327
                        req.store, req.user, worksheet_exercise)
328
1080.1.56 by Matt Giuca
Added new module: ivle.worksheet. This will contain general functions for
329
    # Also get the number of attempts taken and whether this is complete.
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
330
    complete, curctx['attempts'] = \
1099.1.220 by Nick Chadwick
Merged from trunk
331
            ivle.worksheet.utils.get_exercise_status(req.store, req.user, 
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
332
                                               worksheet_exercise)
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
333
    if save is not None:
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
334
        curctx['exercisesave'] = save.text
335
    else:
336
        curctx['exercisesave']= exercise.partial
1099.1.45 by William Grant
ivle.webapp.tutorial: Recapitalise 'Complete' and 'Incomplete' in body text.
337
    curctx['complete'] = 'Complete' if complete else 'Incomplete'
338
    curctx['complete_class'] = curctx['complete'].lower()
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
339
340
    #Save the exercise details to the Table of Contents
341
342
    loader = genshi.template.TemplateLoader(".", auto_reload=True)
1109 by matt.giuca
tutorial/__init__.py: Fixed path to exercises template
343
    tmpl = loader.load(os.path.join(os.path.dirname(__file__),
344
        "templates/exercise.html"))
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
345
    ex_stream = tmpl.generate(curctx)
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
346
    return {'name': exercise.name,
1099.1.45 by William Grant
ivle.webapp.tutorial: Recapitalise 'Complete' and 'Incomplete' in body text.
347
            'complete': curctx['complete_class'],
348
            'stream': ex_stream,
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
349
            'exid': exercise.id}
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
350
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
351
class OfferingAdminView(XHTMLView):
352
    """The admin view for an Offering.
353
    
354
    This class is designed to check the user has admin privileges, and
355
    then allow them to edit the RST for the offering, which controls which
356
    worksheets are actually displayed on the page."""
357
    pass
358
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
359
class WorksheetEditView(XHTMLView):
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
360
    """The admin view for an offering.
361
    
362
    This view is designed to replace worksheets.xml, turning them instead
363
    into XML directly from RST."""
364
    permission = "edit"
1099.1.192 by Nick Chadwick
Moved the tutorial templates in a new directory to keep tutorial cleaner
365
    template = "templates/worksheet_edit.html"
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
366
    tab = "subjects"
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
367
368
    def populate(self, req, ctx):
369
        self.plugin_styles[Plugin] = ["tutorial_admin.css"]
370
        self.plugin_scripts[Plugin] = ['tutorial_admin.js']
1294.2.64 by William Grant
Port tutorial stuff.
371
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
372
        ctx['worksheet'] = self.context
1294.2.64 by William Grant
Port tutorial stuff.
373
        ctx['worksheetname'] = self.context.identifier
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
374
        ctx['subject'] = self.context.offering.subject
1294.2.64 by William Grant
Port tutorial stuff.
375
        ctx['year'] = self.context.offering.semester.year
376
        ctx['semester'] = self.context.offering.semester.semester
1099.1.193 by Nick Chadwick
Modified the edit view of a worksheet to allow editing of the format
377
        #XXX: Get the list of formats from somewhere else
378
        ctx['formats'] = ['xml', 'rst']
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
379
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
380
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
381
class WorksheetAddView(XHTMLView):
382
    """This view allows a user to add a worksheet"""
383
    permission = "edit"
1099.1.192 by Nick Chadwick
Moved the tutorial templates in a new directory to keep tutorial cleaner
384
    template = "templates/worksheet_add.html"
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
385
386
    def populate(self, req, ctx):
387
        self.plugin_styles[Plugin] = ["tutorial_admin.css"]
388
        self.plugin_scripts[Plugin] = ['tutorial_admin.js']
389
        
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
390
        ctx['subject'] = self.context.subject
1294.2.64 by William Grant
Port tutorial stuff.
391
        ctx['year'] = self.context.semester.year
392
        ctx['semester'] = self.context.semester.semester
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
393
        
394
        #XXX: Get the list of formats from somewhere else
395
        ctx['formats'] = ['xml', 'rst']
396
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
397
class WorksheetsEditView(XHTMLView):
398
    """View for arranging worksheets."""
399
    
400
    permission = 'edit'
401
    template = 'templates/worksheets_edit.html'
1294.2.64 by William Grant
Port tutorial stuff.
402
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
403
    def populate(self, req, ctx):
404
        self.plugin_styles[Plugin] = ['tutorial_admin.css']
405
        self.plugin_scripts[Plugin] = ['tutorial_admin.js']
406
        
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
407
        ctx['subject'] = self.context.subject
1294.2.64 by William Grant
Port tutorial stuff.
408
        ctx['year'] = self.context.semester.year
409
        ctx['semester'] = self.context.semester.semester
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
410
        
411
        ctx['worksheets'] = self.context.worksheets
412
        
413
        ctx['mediapath'] = media_url(req, Plugin, 'images/')
1099.1.212 by Nick Chadwick
Added a new page to display exercises. This will then be modified to
414
415
416
class ExerciseEditView(XHTMLView):
417
    """View for editing a worksheet."""
418
    
419
    permission = 'edit'
420
    template = 'templates/exercise_edit.html'
421
    
422
    def populate(self, req, ctx):
423
        self.plugin_styles[Plugin] = ['exercise_admin.css']
424
        self.plugin_scripts[Plugin] = ['exercise_admin.js']
1099.6.4 by Nick Chadwick
Exercise UI is now ready to be merged into trunk.
425
            
426
        ctx['mediapath'] = media_url(req, Plugin, 'images/')
1099.1.212 by Nick Chadwick
Added a new page to display exercises. This will then be modified to
427
        
428
        ctx['exercise'] = self.context
429
        #XXX: These should come from somewhere else
430
431
        ctx['var_types'] = (u'file', u'var', u'arg', u'exception')
432
        ctx['part_types'] = (u'stdout',u'stderr', u'result',
433
                             u'exception', u'file', u'code')
434
        
435
        ctx['test_types'] = ('norm', 'check')
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
436
1099.6.2 by Nick Chadwick
Added a listing of all exercises
437
class ExerciseDeleteView(XHTMLView):
438
    """View for confirming the deletion of an exercise."""
439
    
440
    permission = 'edit'
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
441
    template = 'templates/exercise_delete.html'
1099.6.2 by Nick Chadwick
Added a listing of all exercises
442
    
443
    def populate(self, req, ctx):
1099.1.233 by Nick Chadwick
Exercise objects in the database module, along with their test cases,
444
445
        # If post, delete the exercise, or display a message explaining that
446
        # the exercise cannot be deleted
447
        if req.method == 'POST':
448
            ctx['method'] = 'POST'
1099.1.242 by Nick Chadwick
Fixed a problem with exercise editor, which wasn't editing or adding
449
            try:
450
                self.context.delete()
451
                ctx['deleted'] = True
452
            except:
453
                ctx['deleted'] = False
1099.1.233 by Nick Chadwick
Exercise objects in the database module, along with their test cases,
454
455
        # If get, display a delete confirmation page
456
        else:
457
            ctx['method'] = 'GET'
458
            if self.context.worksheet_exercises.count() is not 0:
459
                ctx['has_worksheets'] = True
460
            else:
461
                ctx['has_worksheets'] = False
462
        # Variables for the template
1099.6.2 by Nick Chadwick
Added a listing of all exercises
463
        ctx['exercise'] = self.context
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
464
        ctx['path'] = "/+exercises/" + self.context.id + "/+delete"
1099.6.2 by Nick Chadwick
Added a listing of all exercises
465
1099.1.229 by Nick Chadwick
Fixed a slight oversight, which meant there was no way to add a new
466
class ExerciseAddView(XHTMLView):
467
    """View for creating a new exercise."""
468
    
469
    permission = 'edit'
470
    template = 'templates/exercise_add.html'
1099.1.234 by Nick Chadwick
Permissions for editing and deleting exercises now come from the
471
    #XXX: This should be done somewhere else
1099.1.229 by Nick Chadwick
Fixed a slight oversight, which meant there was no way to add a new
472
    def authorize(self, req):
473
        for offering in req.store.find(Offering):
474
            if 'edit' in offering.get_permissions(req.user):
475
                return True
476
        return False
477
        
478
    def populate(self, req, ctx):
479
        self.plugin_scripts[Plugin] = ['exercise_admin.js']
480
481
1099.6.2 by Nick Chadwick
Added a listing of all exercises
482
class ExercisesView(XHTMLView):
483
    """View for seeing the list of all exercises"""
484
    
485
    permission = 'edit'
486
    template = 'templates/exercises.html'
1099.1.234 by Nick Chadwick
Permissions for editing and deleting exercises now come from the
487
    #XXX: This should be done somewhere else
1099.6.2 by Nick Chadwick
Added a listing of all exercises
488
    def authorize(self, req):
489
        for offering in req.store.find(Offering):
490
            if 'edit' in offering.get_permissions(req.user):
491
                return True
492
        return False
493
    
494
    def populate(self, req, ctx):
495
        self.plugin_styles[Plugin] = ['exercise_admin.css']
496
        ctx['exercises'] = req.store.find(Exercise).order_by(Exercise.id)
497
        ctx['mediapath'] = media_url(req, Plugin, 'images/')
498
1294.2.64 by William Grant
Port tutorial stuff.
499
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
500
class Plugin(ViewPlugin, MediaPlugin):
1294.2.71 by William Grant
Move i.w.tutorial traversal stuff into i.w.tutorial.traversal.
501
    forward_routes = (root_to_exercise, offering_to_worksheet,
502
        worksheet_to_worksheetexercise, worksheetexercise_to_exerciseattempts,
503
        exerciseattempts_to_attempt)
504
505
    reverse_routes = (exercise_url, worksheet_url, worksheetexercise_url,
506
        exerciseattempts_url, exerciseattempt_url)
1294.2.64 by William Grant
Port tutorial stuff.
507
1294.2.100 by William Grant
Add exercise/worksheet breadcrumbs.
508
    breadcrumbs = {Exercise: ExerciseBreadcrumb,
509
                   DBWorksheet: WorksheetBreadcrumb
510
                  }
511
1294.2.64 by William Grant
Port tutorial stuff.
512
    views = [(Offering, ('+worksheets', '+index'), OfferingView),
513
             (Offering, ('+worksheets', '+new'), WorksheetAddView),
514
             (Offering, ('+worksheets', '+edit'), WorksheetsEditView),
515
             (DBWorksheet, '+index', WorksheetView),
516
             (DBWorksheet, '+edit', WorksheetEditView),
517
             (ApplicationRoot, ('+exercises', '+index'), ExercisesView),
518
             (ApplicationRoot, ('+exercises', '+add'), ExerciseAddView),
519
             (Exercise, '+edit', ExerciseEditView),
520
             (Exercise, '+delete', ExerciseDeleteView),
521
522
             (Offering, ('+worksheets', '+index'), WorksheetsRESTView, 'api'),
523
             (DBWorksheet, '+index', WorksheetRESTView, 'api'),
524
             (WorksheetExercise, '+index', WorksheetExerciseRESTView, 'api'),
525
             (ExerciseAttempts, '+index', AttemptsRESTView, 'api'),
526
             (ExerciseAttempt, '+index', AttemptRESTView, 'api'),
527
             (ApplicationRoot, ('+exercises', '+index'), ExercisesRESTView,
528
              'api'),
529
             (Exercise, '+index', ExerciseRESTView, 'api'),
530
             ]
1099.1.64 by William Grant
Move ivle.webapp.tutorial's media to the new framework. This also fixes the
531
532
    media = 'media'
1099.1.100 by Nick Chadwick
Created a new help system.
533
    help = {'Tutorial': 'help.html'}