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

294 by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for
1
# IVLE - Informatics Virtual Learning Environment
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
2
# Copyright (C) 2007-2009 The University of Melbourne
294 by mattgiuca
Added application: tutorialservice. Will be used as the Ajax backend for
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.70 by William Grant
Clean up ivle.webapp.tutorial.service.
18
# Author: Matt Giuca, Nick Chadwick
19
20
'''AJAX backend for the tutorial application.'''
300 by mattgiuca
conf/apps: Added TutorialService as a registered app.
21
301 by mattgiuca
tutorialservice: Now parses and executes the students code using the test
22
import os
1080.1.58 by Matt Giuca
ivle.worksheet: Added get_exercise_attempts and get_exercise_attempt.
23
import datetime
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
24
import genshi
301 by mattgiuca
tutorialservice: Now parses and executes the students code using the test
25
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
26
import ivle.util
27
import ivle.console
1080.1.58 by Matt Giuca
ivle.worksheet: Added get_exercise_attempts and get_exercise_attempt.
28
import ivle.database
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
29
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
30
                          Offering, Subject, Semester, WorksheetExercise
1080.1.56 by Matt Giuca
Added new module: ivle.worksheet. This will contain general functions for
31
import ivle.worksheet
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
32
import ivle.conf
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
33
import ivle.webapp.tutorial.test
301 by mattgiuca
tutorialservice: Now parses and executes the students code using the test
34
1099.1.113 by William Grant
Give console and tutorial services security declarations.
35
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
36
                                   require_permission)
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
37
from ivle.webapp.errors import NotFound
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
38
1025 by mattgiuca
tutorialservice: Added two new GET actions: getattempts and getattempt.
39
# If True, getattempts or getattempt will allow browsing of inactive/disabled
40
# attempts. If False, will not allow this.
41
HISTORY_ALLOW_INACTIVE = False
42
1080.1.88 by William Grant
ivle.worksheet: Add a save_exercise function.
43
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
44
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
45
class AttemptsRESTView(JSONRESTView):
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
46
    '''REST view of a user's attempts at an exercise.'''
1099.1.192 by Nick Chadwick
Moved the tutorial templates in a new directory to keep tutorial cleaner
47
    
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
48
    def __init__(self, req, subject, year, semester, worksheet, 
49
                                                exercise, username):
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
50
        self.user = ivle.database.User.get_by_login(req.store, username)
51
        if self.user is None:
52
            raise NotFound()
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
53
        
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
54
        self.worksheet_exercise = req.store.find(WorksheetExercise,
55
            WorksheetExercise.exercise_id == exercise,
56
            WorksheetExercise.worksheet_id == Worksheet.id,
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
57
            Worksheet.offering_id == Offering.id,
58
            Offering.subject_id == Subject.id,
59
            Subject.code == subject,
60
            Offering.semester_id == Semester.id,
61
            Semester.year == year,
62
            Semester.semester == semester).one()
63
        
1099.1.113 by William Grant
Give console and tutorial services security declarations.
64
        self.context = self.user # XXX: Not quite right.
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
65
1099.1.113 by William Grant
Give console and tutorial services security declarations.
66
    @require_permission('edit')
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
67
    def GET(self, req):
68
        """Handles a GET Attempts action."""
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
69
        attempts = req.store.find(ExerciseAttempt, 
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
70
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
71
                ExerciseAttempt.user_id == self.user.id)
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
72
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
73
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
74
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
1080.1.58 by Matt Giuca
ivle.worksheet: Added get_exercise_attempts and get_exercise_attempt.
75
                for a in attempts]
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
76
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
77
        return attempts
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
78
79
1099.1.113 by William Grant
Give console and tutorial services security declarations.
80
    @require_permission('edit')
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
81
    def PUT(self, req, data):
1099.1.114 by Nick Chadwick
Modified the database so that exercises are now stored in the database, rather
82
        """ Tests the given submission """
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
83
        exercise = req.store.find(Exercise, 
84
            Exercise.id == self.worksheet_exercise.exercise_id).one()
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
85
        if exercise is None:
1099.1.93 by William Grant
Remove remaining uses of req.throw_error in the new webapps.
86
            raise NotFound()
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
87
88
        # Start a console to run the tests on
89
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
90
        working_dir = os.path.join("/home", req.user.login)
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
91
        cons = ivle.console.Console(req.user.unixid, jail_path, working_dir)
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
92
93
        # Parse the file into a exercise object using the test suite
1099.1.58 by Nick Chadwick
Updated the Worksheets to use a new tutorialservice, hosted in the
94
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
1099.1.141 by Nick Chadwick
Updated the exercises to be loaded from the database, not a local file.
95
                                                            exercise, cons)
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
96
97
        # Run the test cases. Get the result back as a JSONable object.
98
        # Return it.
1099.1.58 by Nick Chadwick
Updated the Worksheets to use a new tutorialservice, hosted in the
99
        test_results = exercise_obj.run_tests(data['code'])
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
100
101
        # Close the console
102
        cons.close()
103
104
        attempt = ivle.database.ExerciseAttempt(user=req.user,
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
105
            worksheet_exercise = self.worksheet_exercise,
106
            date = datetime.datetime.now(),
107
            complete = test_results['passed'],
108
            text = unicode(data['code'])
109
        )
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
110
111
        req.store.add(attempt)
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
112
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
113
        # Query the DB to get an updated score on whether or not this problem
114
        # has EVER been completed (may be different from "passed", if it has
115
        # been completed before), and the total number of attempts.
116
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
117
            req.user, self.worksheet_exercise)
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
118
        test_results["completed"] = completed
119
        test_results["attempts"] = attempts
120
121
        return test_results
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
122
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
123
124
class AttemptRESTView(JSONRESTView):
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
125
    '''REST view of an exercise attempt.'''
126
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
127
    def __init__(self, req, subject, year, semester, worksheet, exercise, 
128
                 username, date):
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
129
        # TODO: Find exercise within worksheet.
130
        user = ivle.database.User.get_by_login(req.store, username)
131
        if user is None:
132
            raise NotFound()
133
1099.1.198 by William Grant
Fix retrieval of old exercise attempts.
134
        try:
135
            date = datetime.datetime.strptime(date, TIMESTAMP_FORMAT)
136
        except ValueError:
137
            raise NotFound()
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
138
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
139
        worksheet_exercise = req.store.find(WorksheetExercise,
140
            WorksheetExercise.exercise_id == exercise,
141
            WorksheetExercise.worksheet_id == Worksheet.id,
142
            Worksheet.identifier == worksheet,
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
143
            Worksheet.offering_id == Offering.id,
144
            Offering.subject_id == Subject.id,
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
145
            Subject.code == subject,
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
146
            Offering.semester_id == Semester.id,
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
147
            Semester.year == year,
148
            Semester.semester == semester).one()
149
            
1099.1.198 by William Grant
Fix retrieval of old exercise attempts.
150
        attempt = ivle.worksheet.get_exercise_attempt(req.store, user,
151
                        worksheet_exercise, as_of=date,
152
                        allow_inactive=HISTORY_ALLOW_INACTIVE) 
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
153
154
        if attempt is None:
155
            raise NotFound()
156
157
        self.context = attempt
158
1099.1.113 by William Grant
Give console and tutorial services security declarations.
159
    @require_permission('view')
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
160
    def GET(self, req):
161
        return {'code': self.context.text}
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
162
163
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
164
class ExerciseRESTView(JSONRESTView):
1099.1.70 by William Grant
Clean up ivle.webapp.tutorial.service.
165
    '''REST view of an exercise.'''
1099.1.113 by William Grant
Give console and tutorial services security declarations.
166
167
    def get_permissions(self, user):
168
        # XXX: Do it properly.
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
169
        # XXX: Does any user have the ability to save as themselves?
170
        # XXX: Does a user EVER have permission to save as another user?
1099.1.113 by William Grant
Give console and tutorial services security declarations.
171
        if user is not None:
172
            return set(['save'])
173
        else:
174
            return set()
175
176
    @named_operation('save')
1099.1.58 by Nick Chadwick
Updated the Worksheets to use a new tutorialservice, hosted in the
177
    def save(self, req, text):
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
178
        # Find the appropriate WorksheetExercise to save to. If its not found,
179
        # the user is submitting against a non-existant worksheet/exercise
180
        worksheet_exercise = req.store.find(WorksheetExercise,
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
181
            WorksheetExercise.exercise_id == self.exercise,
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
182
            WorksheetExercise.worksheet_id == Worksheet.id,
1099.1.150 by Nick Chadwick
Modified worksheets to properly link attempts to worksheets and
183
            Worksheet.offering_id == Offering.id,
184
            Offering.subject_id == Subject.id,
185
            Subject.code == self.subject,
186
            Offering.semester_id == Semester.id,
187
            Semester.year == self.year,
188
            Semester.semester == self.semester).one()
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
189
        
190
        if worksheet_exercise is None:
191
            raise NotFound()
192
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
193
        old_save = req.store.find(ExerciseSave,
194
            ExerciseSave.ws_ex_id == worksheet_exercise.id,
195
            ExerciseSave.user == req.user).one()
196
        
197
        #Overwrite the old, or create a new if there isn't one
198
        if old_save is None:
199
            new_save = ExerciseSave()
200
            req.store.add(new_save)
201
        else:
202
            new_save = old_save
203
        
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
204
        new_save.worksheet_exercise = worksheet_exercise
205
        new_save.user = req.user
206
        new_save.text = unicode(text)
207
        new_save.date = datetime.datetime.now()
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
208
1099.1.49 by Nick Chadwick
Began moving tutorialservice over to webapp.
209
        return {"result": "ok"}
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
210
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
211
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
212
def generate_exerciselist(worksheet, req, worksheetdata):
213
    """Runs through the worksheetstream, generating the appropriate
214
    WorksheetExercises, and de-activating the old ones."""
215
    exercises = []
216
    # Turns the worksheet into an xml stream, and then finds all the 
217
    # exercise nodes in the stream.
218
    worksheetdata = genshi.XML(worksheetdata)
219
    for kind, data, pos in worksheetdata:
220
        if kind is genshi.core.START:
221
            # Data is a tuple of tag name and a list of name->value tuples
222
            if data[0] == 'exercise':
223
                src = ""
224
                optional = False
225
                for attr in data[1]:
226
                    if attr[0] == 'src':
227
                        src = attr[1]
228
                    if attr[0] == 'optional':
229
                        optional = attr[1] == 'true'
230
                if src != "":
231
                    exercises.append((src, optional))
232
    ex_num = 0
233
    # Set all current worksheet_exercises to be inactive
234
    db_worksheet_exercises = req.store.find(WorksheetExercise,
235
        WorksheetExercise.worksheet_id == worksheet.id)
236
    for worksheet_exercise in db_worksheet_exercises:
237
        worksheet_exercise.active = False
238
    
239
    for exerciseid, optional in exercises:
240
        worksheet_exercise = req.store.find(WorksheetExercise,
241
            WorksheetExercise.worksheet_id == worksheet.id,
242
            Exercise.id == WorksheetExercise.exercise_id,
243
            Exercise.id == exerciseid).one()
244
        if worksheet_exercise is None:
245
            exercise = req.store.find(Exercise,
246
                Exercise.id == exerciseid
247
            ).one()
248
            if exercise is None:
249
                raise NotFound()
250
            worksheet_exercise = WorksheetExercise()
251
            worksheet_exercise.worksheet_id = worksheet.id
252
            worksheet_exercise.exercise_id = exercise.id
253
            req.store.add(worksheet_exercise)
254
        worksheet_exercise.active = True
255
        worksheet_exercise.seq_no = ex_num
256
        worksheet_exercise.optional = optional
257
258
259
# Note that this is the view of an existing worksheet. Creation is handled
260
# by OfferingRESTView (as offerings have worksheets)
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
261
class WorksheetRESTView(JSONRESTView):
262
    """View used to update a worksheet."""
263
264
    def get_permissions(self, user):
265
        # XXX: Do it properly.
1099.1.180 by Nick Chadwick
This commit changes the tutorial service, which now almost exclusively
266
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
267
        #      under their control
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
268
        if user is not None:
269
            if user.rolenm == 'admin':
270
                return set(['save'])
271
            else:
272
                return set()
273
        else:
274
            return set()    
275
276
    def __init__(self, req, **kwargs):
277
    
278
        self.worksheet = kwargs['worksheet']
279
        self.subject = kwargs['subject']
280
        self.year = kwargs['year']
281
        self.semester = kwargs['semester']
282
    
283
        self.context = req.store.find(Worksheet,
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
284
            Worksheet.identifier == self.worksheet,
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
285
            Worksheet.offering_id == Offering.id,
286
            Offering.subject_id == Subject.id,
287
            Subject.code == self.subject,
288
            Offering.semester_id == Semester.id,
289
            Semester.year == self.year,
290
            Semester.semester == self.semester).one()
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
291
        
292
        if self.context is None:
293
            raise NotFound()
1099.4.1 by Nick Chadwick
Working on putting worksheets into the database.
294
    
295
    @named_operation('save')
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
296
    def save(self, req, name, assessable, data, format):
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
297
        """Takes worksheet data and saves it."""
1099.1.194 by Nick Chadwick
Updated worksheet_edit to correctly set the format.
298
        generate_exerciselist(self.context, req, data)
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
299
        
300
        self.context.name = unicode(name)
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
301
        self.context.assessable = self.convert_bool(assessable)
1099.4.3 by Nick Chadwick
Updated the tutorial service, to now allow users to edit worksheets
302
        self.context.data = unicode(data)
1099.1.194 by Nick Chadwick
Updated worksheet_edit to correctly set the format.
303
        self.context.format = unicode(format)
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
304
        
305
        return {"result": "ok"}
306
1099.1.189 by Nick Chadwick
Updated service.py to correctly add a seq_no to a worksheet before
307
class WorksheetsRESTView(JSONRESTView):
308
    """View used to update and create Worksheets."""
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
309
    
310
    def get_permissions(self, user):
311
        # XXX: Do it properly.
312
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
313
        #      under their control
314
        if user is not None:
315
            if user.rolenm == 'admin':
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
316
                return set(['edit'])
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
317
            else:
318
                return set()
319
        else:
320
            return set()
321
322
    def __init__(self, req, **kwargs):
323
    
324
        self.subject = kwargs['subject']
325
        self.year = kwargs['year']
326
        self.semester = kwargs['semester']
327
    
328
        self.context = req.store.find(Offering,
329
            Offering.subject_id == Subject.id,
330
            Subject.code == self.subject,
331
            Offering.semester_id == Semester.id,
332
            Semester.year == self.year,
333
            Semester.semester == self.semester).one()
334
        
335
        if self.context is None:
336
            raise NotFound()
337
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
338
    @named_operation('edit')
1099.1.189 by Nick Chadwick
Updated service.py to correctly add a seq_no to a worksheet before
339
    def add_worksheet(self, req, identifier, name, assessable, data, format):
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
340
        """Takes worksheet data and adds it."""
341
        
342
        new_worksheet = Worksheet()
1099.1.189 by Nick Chadwick
Updated service.py to correctly add a seq_no to a worksheet before
343
        new_worksheet.seq_no = self.context.worksheets.count()
344
        # Setting new_worksheet.offering implicitly adds new_worksheet,
345
        # hence worksheets.count MUST be called above it
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
346
        new_worksheet.offering = self.context
347
        new_worksheet.identifier = unicode(identifier)
348
        new_worksheet.name = unicode(name)
349
        new_worksheet.assessable = self.convert_bool(assessable)
350
        new_worksheet.data = unicode(data)
351
        new_worksheet.format = unicode(format)
1099.1.189 by Nick Chadwick
Updated service.py to correctly add a seq_no to a worksheet before
352
        
353
        # This call is added for clarity, as the worksheet is implicitly added.        
1099.1.182 by Nick Chadwick
Added a view to allow admins to edit worksheets
354
        req.store.add(new_worksheet)
355
        
356
        generate_exerciselist(new_worksheet, req, data)
357
        
358
        return {"result": "ok"}
359
1099.1.197 by Nick Chadwick
Modified worksheets edit view, so now there are links to edit, add,
360
    @named_operation('edit')
361
    def move_up(self, req, worksheetid):
362
        """Takes a list of worksheet-seq_no pairs and updates their 
363
        corresponding Worksheet objects to match."""
364
        
365
        worksheet_below = req.store.find(Worksheet,
366
            Worksheet.offering_id == self.context.id,
367
            Worksheet.identifier == unicode(worksheetid)).one()
368
        if worksheet_below is None:
369
            raise NotFound('worksheet_below')
370
        worksheet_above = req.store.find(Worksheet,
371
            Worksheet.offering_id == self.context.id,
372
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
373
        if worksheet_above is None:
374
            raise NotFound('worksheet_above')
375
376
        worksheet_below.seq_no = worksheet_below.seq_no - 1
377
        worksheet_above.seq_no = worksheet_above.seq_no + 1
378
        
379
        return {'result': 'ok'}
380
381
    @named_operation('edit')
382
    def move_down(self, req, worksheetid):
383
        """Takes a list of worksheet-seq_no pairs and updates their 
384
        corresponding Worksheet objects to match."""
385
        
386
        worksheet_above = req.store.find(Worksheet,
387
            Worksheet.offering_id == self.context.id,
388
            Worksheet.identifier == unicode(worksheetid)).one()
389
        if worksheet_above is None:
390
            raise NotFound('worksheet_below')
391
        worksheet_below = req.store.find(Worksheet,
392
            Worksheet.offering_id == self.context.id,
393
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
394
        if worksheet_below is None:
395
            raise NotFound('worksheet_above')
396
397
        worksheet_below.seq_no = worksheet_below.seq_no - 1
398
        worksheet_above.seq_no = worksheet_above.seq_no + 1
1099.1.189 by Nick Chadwick
Updated service.py to correctly add a seq_no to a worksheet before
399
        
400
        return {'result': 'ok'}