~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
1099.1.36 by William Grant
ivle.webapp.tutorial: Clean up.
35
import ivle.util
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
36
import ivle.conf
1080.1.32 by me at id
www/app/{subjects,tutorial}: Use the new Storm API to get enrolled subjects.
37
import ivle.database
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
38
from ivle.database import Subject, Offering, Semester, Exercise, \
39
                          ExerciseSave, WorksheetExercise
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
40
from ivle.database import Worksheet as DBWorksheet
1080.1.56 by Matt Giuca
Added new module: ivle.worksheet. This will contain general functions for
41
import ivle.worksheet
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
42
from ivle.webapp.base.views import BaseView
43
from ivle.webapp.base.xhtml import XHTMLView
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
44
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
45
from ivle.webapp.media import BaseMediaFileView, media_url
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
46
from ivle.webapp.errors import NotFound, Forbidden
1099.1.89 by William Grant
Don't shadow ivle.webapp.tutorial.rst (we were importing
47
from ivle.webapp.tutorial.rst import rst as rstfunc
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
48
from ivle.webapp.tutorial.service import AttemptsRESTView, AttemptRESTView, \
1099.1.191 by William Grant
Complete the rename of OfferingRESTView.
49
                      ExerciseRESTView, WorksheetRESTView, WorksheetsRESTView
523 by stevenbird
Adding ReStructured Text preprocessing of exercise descriptions,
50
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
51
class Worksheet:
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
52
    """This class represents a worksheet and a particular students progress
53
    through it.
54
    
55
    Do not confuse this with a worksheet in the database. This worksheet
56
    has extra information for use in the output, such as marks."""
734 by mattgiuca
tutorial: BEHAVIOUR CHANGE
57
    def __init__(self, id, name, assessable):
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
58
        self.id = id
59
        self.name = name
734 by mattgiuca
tutorial: BEHAVIOUR CHANGE
60
        self.assessable = assessable
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
61
        self.complete_class = ''
62
        self.optional_message = ''
63
        self.total = 0
64
        self.mand_done = 0
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
65
    def __repr__(self):
734 by mattgiuca
tutorial: BEHAVIOUR CHANGE
66
        return ("Worksheet(id=%s, name=%s, assessable=%s)"
67
                % (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
68
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
69
class OfferingView(XHTMLView):
70
    '''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
71
    template = 'templates/subjectmenu.html'
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
72
    appname = 'tutorial' # XXX
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
73
    permission = 'view'
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
74
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
75
    def __init__(self, req, subject, year, semester):
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
76
        """Find the given offering by subject, year and semester."""
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
77
        self.context = req.store.find(Offering,
78
            Offering.subject_id == Subject.id,
79
            Subject.code == subject,
80
            Offering.semester_id == Semester.id,
81
            Semester.year == year,
82
            Semester.semester == semester).one()
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
83
        
84
        if not self.context:
85
            raise NotFound()
86
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
87
88
    def populate(self, req, ctx):
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
89
        """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
90
        self.plugin_styles[Plugin] = ['tutorial.css']
91
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
92
        ctx['subject'] = self.context.subject
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
93
        ctx['year'] = self.context.semester.year
94
        ctx['semester'] = self.context.semester.semester
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
95
96
        # As we go, calculate the total score for this subject
97
        # (Assessable worksheets only, mandatory problems only)
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
98
99
        ctx['worksheets'] = []
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
100
        problems_done = 0
101
        problems_total = 0
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
102
        # Offering.worksheets is ordered by the worksheets seq_no
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
103
        for worksheet in self.context.worksheets:
104
            new_worksheet = Worksheet(worksheet.identifier, worksheet.name, 
105
                                      worksheet.assessable)
106
            if new_worksheet.assessable:
107
                # Calculate the user's score for this worksheet
108
                mand_done, mand_total, opt_done, opt_total = (
109
                    ivle.worksheet.calculate_score(req.store, req.user,
110
                        worksheet))
111
                if opt_total > 0:
112
                    optional_message = " (excluding optional exercises)"
113
                else:
114
                    optional_message = ""
115
                if mand_done >= mand_total:
116
                    new_worksheet.complete_class = "complete"
117
                elif mand_done > 0:
118
                    new_worksheet.complete_class = "semicomplete"
119
                else:
120
                    new_worksheet.complete_class = "incomplete"
121
                problems_done += mand_done
122
                problems_total += mand_total
123
                new_worksheet.mand_done = mand_done
124
                new_worksheet.total = mand_total
125
                new_worksheet.optional_message = optional_message
126
            ctx['worksheets'].append(new_worksheet)
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
127
128
        ctx['problems_total'] = problems_total
129
        ctx['problems_done'] = problems_done
130
        if problems_total > 0:
131
            if problems_done >= problems_total:
132
                ctx['complete_class'] = "complete"
133
            elif problems_done > 0:
134
                ctx['complete_class'] = "semicomplete"
135
            else:
136
                ctx['complete_class'] = "incomplete"
137
            ctx['problems_pct'] = (100 * problems_done) / problems_total
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
138
139
            # We want to display a students mark out of 5. However, they are
140
            # allowed to skip 1 in 5 questions and still get 'full marks'.
141
            # Hence we divide by 16, essentially making 16 percent worth
142
            # 1 star, and 80 or above worth 5.
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
143
            ctx['max_mark'] = 5
144
            ctx['mark'] = min(ctx['problems_pct'] / 16, ctx['max_mark'])
145
146
class WorksheetView(XHTMLView):
147
    '''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
148
    template = 'templates/worksheet.html'
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
149
    appname = 'tutorial' # XXX
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
150
    permission = 'view'
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
151
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
152
    def __init__(self, req, subject, year, semester, worksheet):
153
        self.context = req.store.find(DBWorksheet,
154
            DBWorksheet.offering_id == Offering.id,
155
            Offering.subject_id == Subject.id,
156
            Subject.code == subject,
157
            Offering.semester_id == Semester.id,
158
            Semester.year == year,
159
            Semester.semester == semester,
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
160
            DBWorksheet.identifier == worksheet).one()
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
161
        
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
162
        if self.context is None:
163
            raise NotFound(str(worksheet) + " was not found.")
164
        
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
165
        self.year = year
166
        self.semester = semester
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
167
168
    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
169
        self.plugin_scripts[Plugin] = ['tutorial.js']
170
        self.plugin_styles[Plugin] = ['tutorial.css']
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
171
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
172
        if not self.context:
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
173
            raise NotFound()
174
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
175
        ctx['subject'] = self.context.offering.subject
176
        ctx['worksheet'] = self.context
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
177
        ctx['semester'] = self.semester
178
        ctx['year'] = self.year
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
179
        ctx['worksheetstream'] = genshi.Stream(list(genshi.XML(self.context.data)))
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
180
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
181
        generate_worksheet_data(ctx, req, self.context)
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
182
183
        ctx['worksheetstream'] = add_exercises(ctx['worksheetstream'], ctx, req)
184
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
185
class SubjectMediaView(BaseMediaFileView):
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
186
    '''The view of subject media files.
187
188
    URIs pointing here will just be served directly, from the subject's
189
    media directory.
190
    '''
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
191
    permission = 'view'
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
192
193
    def __init__(self, req, subject, path):
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
194
        self.context = req.store.find(Subject, code=subject).one()
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
195
        self.path = os.path.normpath(path)
196
1099.1.65 by William Grant
ivle.webapp.tutorial#SubjectMediaView now subclasses
197
    def _make_filename(self, req):
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
198
        # If the subject doesn't exist, self.subject will be None. Die.
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
199
        if not self.context:
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
200
            raise NotFound()
201
202
        subjectdir = os.path.join(ivle.conf.subjects_base,
1099.1.110 by William Grant
Implement an authorization system in the new framework. This breaks the REST
203
                                  self.context.code, 'media')
1099.1.65 by William Grant
ivle.webapp.tutorial#SubjectMediaView now subclasses
204
        return os.path.join(subjectdir, self.path)
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
205
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
206
def get_worksheets(subjectfile):
207
    '''Given a subject stream, get all the worksheets and put them in ctx'''
208
    worksheets = []
209
    for kind, data, pos in subjectfile:
210
        if kind is genshi.core.START:
211
            if data[0] == 'worksheet':
212
                worksheetid = ''
213
                worksheetname = ''
214
                worksheetasses = False
215
                for attr in data[1]:
216
                    if attr[0] == 'id':
217
                        worksheetid = attr[1]
218
                    elif attr[0] == 'name':
219
                        worksheetname = attr[1]
220
                    elif attr[0] == 'assessable':
221
                        worksheetasses = attr[1] == 'true'
222
                worksheets.append(Worksheet(worksheetid, worksheetname, \
223
                                                            worksheetasses))
224
    return worksheets
225
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
226
# 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.
227
def add_exercises(stream, ctx, req):
1099.1.42 by Nick Chadwick
Fixed an oversight in the tutorial code which was printing <worksheet>
228
    """A filter which adds exercises into the stream."""
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
229
    exid = 0
230
    for kind, data, pos in stream:
231
        if kind is genshi.core.START:
1099.1.42 by Nick Chadwick
Fixed an oversight in the tutorial code which was printing <worksheet>
232
            # Remove the worksheet tags, as they are not xhtml valid.
233
            if data[0] == 'worksheet':
234
                continue
235
            # If we have an exercise node, replace it with the content of the
236
            # exercise.
237
            elif data[0] == 'exercise':
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
238
                # XXX: Note that we presume ctx['exercises'] has a correct list
239
                #      of exercises. If it doesn't, something has gone wrong.
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
240
                new_stream = ctx['exercises'][exid]['stream']
241
                exid += 1
242
                for item in new_stream:
243
                    yield item
244
            else:
245
                yield kind, data, pos
1099.1.42 by Nick Chadwick
Fixed an oversight in the tutorial code which was printing <worksheet>
246
        # Remove the end tags for exercises and worksheets
247
        elif kind is genshi.core.END:
248
            if data == 'exercise':
249
                continue
250
            elif data == 'worksheet':
251
                continue
252
            else:
253
                yield kind, data, pos
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
254
        else:
255
            yield kind, data, pos
256
257
# This function runs through the worksheet, to get data on the exercises to
258
# 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
259
def generate_worksheet_data(ctx, req, worksheet):
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
260
    """Runs through the worksheetstream, generating the exericises"""
261
    ctx['exercises'] = []
262
    ctx['exerciselist'] = []
263
    for kind, data, pos in ctx['worksheetstream']:
264
        if kind is genshi.core.START:
265
            if data[0] == 'exercise':
266
                src = ""
267
                optional = False
268
                for attr in data[1]:
269
                    if attr[0] == 'src':
270
                        src = attr[1]
271
                    if attr[0] == 'optional':
272
                        optional = attr[1] == 'true'
273
                # 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
274
                if src != "":
275
                    ctx['exercises'].append(present_exercise(req, src, worksheet))
276
                    ctx['exerciselist'].append((src, optional))
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
277
            elif data[0] == 'worksheet':
278
                ctx['worksheetname'] = 'bob'
279
                for attr in data[1]:
280
                    if attr[0] == 'name':
281
                        ctx['worksheetname'] = attr[1]
425 by mattgiuca
tutorial: Refactored present_worksheet so it has a separate function for
282
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
283
def innerXML(elem):
284
    """Given an element, returns its children as XML strings concatenated
285
    together."""
286
    s = ""
287
    for child in elem.childNodes:
288
        s += child.toxml()
289
    return s
290
291
def getTextData(element):
292
    """ Get the text and cdata inside an element
293
    Leading and trailing whitespace are stripped
294
    """
295
    data = ''
296
    for child in element.childNodes:
297
        if child.nodeType == child.CDATA_SECTION_NODE:
298
            data += child.data
710 by mattgiuca
Tutorial: The tutorial system now presents a table of contents at the top.
299
        elif child.nodeType == child.TEXT_NODE:
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
300
            data += child.data
710 by mattgiuca
Tutorial: The tutorial system now presents a table of contents at the top.
301
        elif child.nodeType == child.ELEMENT_NODE:
302
            data += getTextData(child)
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
303
304
    return data.strip()
305
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
306
def present_exercise(req, src, worksheet):
515 by stevenbird
Propagated "problem" -> "exercise" nomenclature change.
307
    """Open a exercise file, and write out the exercise to the request in HTML.
308
    exercisesrc: "src" of the exercise file. A path relative to the top-level
309
        exercises base directory, as configured in conf.
291 by mattgiuca
tutorial: Added code to handle top-level menu and subject menu (reads dir
310
    """
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
311
    # Exercise-specific context is used here, as we already have all the data
312
    # we need
313
    curctx = genshi.template.Context()
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
314
315
    worksheet_exercise = req.store.find(WorksheetExercise,
316
        WorksheetExercise.worksheet_id == worksheet.id,
317
        WorksheetExercise.exercise_id == src).one()
318
319
    if worksheet_exercise is None:
320
        raise NotFound()
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
321
322
    # Retrieve the exercise details from the database
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
323
    exercise = req.store.find(Exercise, 
324
        Exercise.id == worksheet_exercise.exercise_id).one()
325
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
326
    if exercise is None:
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
327
        raise NotFound(exercisesrc)
1099.1.93 by William Grant
Remove remaining uses of req.throw_error in the new webapps.
328
515 by stevenbird
Propagated "problem" -> "exercise" nomenclature change.
329
    # Read exercise file and present the exercise
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
330
    # Note: We do not use the testing framework because it does a lot more
515 by stevenbird
Propagated "problem" -> "exercise" nomenclature change.
331
    # 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
332
    # fields from the XML.
333
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
334
    #TODO: Replace calls to minidom with calls to the database directly
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
335
    curctx['exercise'] = exercise
336
    if exercise.description is not None:
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
337
        desc = rstfunc(exercise.description)
338
        curctx['description'] = genshi.XML('<div id="description">' + desc + 
339
                                           '</div>')
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
340
    else:
341
        curctx['description'] = None
297 by mattgiuca
tutorial: Now presents problems correctly, by parsing XML source and writing
342
702 by mattgiuca
tutorial:
343
    # If the user has already saved some text for this problem, or submitted
344
    # 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
345
    # Get exercise stored text will return a save, or the most recent attempt,
346
    # whichever is more recent
347
    save = ivle.worksheet.get_exercise_stored_text(
348
                        req.store, req.user, worksheet_exercise)
349
1080.1.56 by Matt Giuca
Added new module: ivle.worksheet. This will contain general functions for
350
    # Also get the number of attempts taken and whether this is complete.
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
351
    complete, curctx['attempts'] = \
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
352
            ivle.worksheet.get_exercise_status(req.store, req.user, 
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
353
                                               worksheet_exercise)
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
354
    if save is not None:
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
355
        curctx['exercisesave'] = save.text
356
    else:
357
        curctx['exercisesave']= exercise.partial
1099.1.45 by William Grant
ivle.webapp.tutorial: Recapitalise 'Complete' and 'Incomplete' in body text.
358
    curctx['complete'] = 'Complete' if complete else 'Incomplete'
359
    curctx['complete_class'] = curctx['complete'].lower()
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
360
361
    #Save the exercise details to the Table of Contents
362
363
    loader = genshi.template.TemplateLoader(".", auto_reload=True)
1099.1.23 by root
ivle.webapp.tutorial#present_exericse: Use the new template path.
364
    tmpl = loader.load(os.path.join(os.path.dirname(__file__), "exercise.html"))
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
365
    ex_stream = tmpl.generate(curctx)
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
366
    return {'name': exercise.name,
1099.1.45 by William Grant
ivle.webapp.tutorial: Recapitalise 'Complete' and 'Incomplete' in body text.
367
            'complete': curctx['complete_class'],
368
            'stream': ex_stream,
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
369
            'exid': exercise.id}
1093 by chadnickbok
Adding the changes from my genshi branch into trunk.
370
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
371
class OfferingAdminView(XHTMLView):
372
    """The admin view for an Offering.
373
    
374
    This class is designed to check the user has admin privileges, and
375
    then allow them to edit the RST for the offering, which controls which
376
    worksheets are actually displayed on the page."""
377
    pass
378
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
379
class WorksheetEditView(XHTMLView):
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
380
    """The admin view for an offering.
381
    
382
    This view is designed to replace worksheets.xml, turning them instead
383
    into XML directly from RST."""
384
    permission = "edit"
1099.1.192 by Nick Chadwick
Moved the tutorial templates in a new directory to keep tutorial cleaner
385
    template = "templates/worksheet_edit.html"
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
386
    appname = "Edit Worksheet"
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
387
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
388
    def __init__(self, req, **kwargs):
389
    
390
        subject = kwargs['subject']
391
        year = kwargs['year']
392
        semester = kwargs['semester']
393
        worksheet = kwargs['worksheet']
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
394
        self.context = req.store.find(DBWorksheet,
395
            DBWorksheet.identifier == worksheet,
396
            DBWorksheet.offering_id == Offering.id,
397
            Offering.semester_id == Semester.id,
398
            Semester.year == year,
399
            Semester.semester == semester,
400
            Offering.subject_id == Subject.id,
401
            Subject.code == subject
402
        ).one()
403
        
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
404
        if self.context is None:
405
            raise NotFound()
406
        
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
407
        self.subject = subject
408
        self.year = year
409
        self.semester = semester
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
410
        self.worksheet = worksheet
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
411
        
412
            
413
    def populate(self, req, ctx):
414
        self.plugin_styles[Plugin] = ["tutorial_admin.css"]
415
        self.plugin_scripts[Plugin] = ['tutorial_admin.js']
416
        
417
        ctx['worksheet'] = self.context
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
418
        ctx['worksheetname'] = self.worksheet
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
419
        ctx['subject'] = self.context.offering.subject
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
420
        ctx['year'] = self.year
421
        ctx['semester'] = self.semester
1099.1.193 by Nick Chadwick
Modified the edit view of a worksheet to allow editing of the format
422
        #XXX: Get the list of formats from somewhere else
423
        ctx['formats'] = ['xml', 'rst']
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
424
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
425
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
426
class WorksheetAddView(XHTMLView):
427
    """This view allows a user to add a worksheet"""
428
    permission = "edit"
1099.1.192 by Nick Chadwick
Moved the tutorial templates in a new directory to keep tutorial cleaner
429
    template = "templates/worksheet_add.html"
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
430
431
    def __init__(self, req, subject, year, semester):
432
        self.context = req.store.find(Offering,
433
            Offering.semester_id == Semester.id,
434
            Semester.year == year,
435
            Semester.semester == semester,
436
            Offering.subject_id == Subject.id,
437
            Subject.code == subject
438
        ).one()
439
        
440
        self.subject = subject
441
        self.year = year
442
        self.semester = semester
443
        
444
        if self.context is None:
445
            raise NotFound()
446
            
447
    def populate(self, req, ctx):
448
        self.plugin_styles[Plugin] = ["tutorial_admin.css"]
449
        self.plugin_scripts[Plugin] = ['tutorial_admin.js']
450
        
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
451
        ctx['subject'] = self.context.subject
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
452
        ctx['year'] = self.year
453
        ctx['semester'] = self.semester
454
        
455
        #XXX: Get the list of formats from somewhere else
456
        ctx['formats'] = ['xml', 'rst']
457
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
458
class WorksheetsEditView(XHTMLView):
459
    """View for arranging worksheets."""
460
    
461
    permission = 'edit'
462
    template = 'templates/worksheets_edit.html'
463
    
464
    def __init__(self, req, subject, year, semester):
465
        self.context = req.store.find(Offering,
466
            Offering.semester_id == Semester.id,
467
            Semester.year == year,
468
            Semester.semester == semester,
469
            Offering.subject_id == Subject.id,
470
            Subject.code == subject
471
        ).one()
472
        
473
        self.subject = subject
474
        self.year = year
475
        self.semester = semester
476
        
477
        if self.context is None:
478
            raise NotFound()
479
    
480
    def populate(self, req, ctx):
481
        self.plugin_styles[Plugin] = ['tutorial_admin.css']
482
        self.plugin_scripts[Plugin] = ['tutorial_admin.js']
483
        
1099.1.207 by William Grant
Replace most of the tutorial headings and titles.
484
        ctx['subject'] = self.context.subject
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
485
        ctx['year'] = self.year
486
        ctx['semester'] = self.semester
487
        
488
        ctx['worksheets'] = self.context.worksheets
489
        
490
        ctx['mediapath'] = media_url(req, Plugin, 'images/')
491
        
492
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
493
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
494
class Plugin(ViewPlugin, MediaPlugin):
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
495
    urls = [
1099.1.199 by William Grant
Move WorksheetAddView to +new (was +add), and clean it up a bit.
496
        ('subjects/:subject/+worksheets/+media/*(path)', SubjectMediaView),
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
497
        ('subjects/:subject/:year/:semester/+worksheets', OfferingView),
1099.1.199 by William Grant
Move WorksheetAddView to +new (was +add), and clean it up a bit.
498
        ('subjects/:subject/:year/:semester/+worksheets/+new', WorksheetAddView),
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
499
        ('subjects/:subject/:year/:semester/+worksheets/+edit', WorksheetsEditView),
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
500
        ('subjects/:subject/:year/:semester/+worksheets/:worksheet', WorksheetView),
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
501
        ('subjects/:subject/:year/:semester/+worksheets/:worksheet/+edit', WorksheetEditView),
1099.1.190 by Nick Chadwick
Renamed OfferingRESTView to WorksheetsRESTView
502
        ('api/subjects/:subject/:year/:semester/+worksheets', WorksheetsRESTView),
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
503
        ('api/subjects/:subject/:year/:semester/+worksheets/:worksheet/*exercise/'
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
504
            '+attempts/:username', AttemptsRESTView),
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
505
        ('api/subjects/:subject/:year/:semester/+worksheets/:worksheet/*exercise/'
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
506
                '+attempts/:username/:date', AttemptRESTView),
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
507
        ('api/subjects/:subject/:year/:semester/+worksheets/:worksheet', WorksheetRESTView),
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
508
        ('api/subjects/:subject/:year/:semester/+worksheets/:worksheet/*exercise', ExerciseRESTView),
1099.1.19 by William Grant
ivle.webapp.tutorial: Port www/apps/tutorial to new framework.
509
    ]
1099.1.64 by William Grant
Move ivle.webapp.tutorial's media to the new framework. This also fixes the
510
511
    media = 'media'
1099.1.100 by Nick Chadwick
Created a new help system.
512
    help = {'Tutorial': 'help.html'}