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

« back to all changes in this revision

Viewing changes to ivle/webapp/tutorial/service.py

  • Committer: Nick Chadwick
  • Date: 2009-02-19 05:47:56 UTC
  • mto: (1099.1.180 new-dispatch)
  • mto: This revision was merged to the branch mainline in revision 1100.
  • Revision ID: chadnickbok@gmail.com-20090219054756-v984vmc7kheiq6xy
Updated the tutorial service, to now allow users to edit worksheets
online.

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
 
45
45
class AttemptsRESTView(JSONRESTView):
46
46
    '''REST view of a user's attempts at an exercise.'''
47
 
    
48
47
    def __init__(self, req, subject, year, semester, worksheet, 
49
48
                                                exercise, username):
50
49
        self.user = ivle.database.User.get_by_login(req.store, username)
51
50
        if self.user is None:
52
51
            raise NotFound()
 
52
        self.exercise = exercise
53
53
        
54
 
        self.worksheet_exercise = req.store.find(WorksheetExercise,
55
 
            WorksheetExercise.exercise_id == exercise,
56
 
            WorksheetExercise.worksheet_id == Worksheet.id,
 
54
        self.worksheet = req.store.find(Worksheet,
 
55
            Worksheet.name == worksheet,
57
56
            Worksheet.offering_id == Offering.id,
58
57
            Offering.subject_id == Subject.id,
59
58
            Subject.code == subject,
66
65
    @require_permission('edit')
67
66
    def GET(self, req):
68
67
        """Handles a GET Attempts action."""
 
68
        exercise = req.store.find(Exercise, Exercise.id == self.exercise).one()
 
69
 
69
70
        attempts = req.store.find(ExerciseAttempt, 
70
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
 
71
                ExerciseAttempt.exercise_id == exercise.id,
71
72
                ExerciseAttempt.user_id == self.user.id)
72
73
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
73
74
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
80
81
    @require_permission('edit')
81
82
    def PUT(self, req, data):
82
83
        """ Tests the given submission """
83
 
        exercise = req.store.find(Exercise, 
84
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
 
84
        exercise = req.store.find(Exercise, Exercise.id == self.exercise).one()
85
85
        if exercise is None:
86
86
            raise NotFound()
87
87
 
102
102
        cons.close()
103
103
 
104
104
        attempt = ivle.database.ExerciseAttempt(user=req.user,
105
 
            worksheet_exercise = self.worksheet_exercise,
106
 
            date = datetime.datetime.now(),
107
 
            complete = test_results['passed'],
108
 
            text = unicode(data['code'])
109
 
        )
 
105
                                                exercise=exercise,
 
106
                                                date=datetime.datetime.now(),
 
107
                                                complete=test_results['passed'],
 
108
                                                worksheet=self.worksheet,
 
109
                                                text=unicode(data['code']))
110
110
 
111
111
        req.store.add(attempt)
112
112
 
114
114
        # has EVER been completed (may be different from "passed", if it has
115
115
        # been completed before), and the total number of attempts.
116
116
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
117
 
            req.user, self.worksheet_exercise)
 
117
            req.user, exercise, self.worksheet)
118
118
        test_results["completed"] = completed
119
119
        test_results["attempts"] = attempts
120
120
 
124
124
class AttemptRESTView(JSONRESTView):
125
125
    '''REST view of an exercise attempt.'''
126
126
 
127
 
    def __init__(self, req, subject, year, semester, worksheet, exercise, 
128
 
                 username, date):
 
127
    def __init__(self, req, subject, worksheet, exercise, username, date):
129
128
        # TODO: Find exercise within worksheet.
130
129
        user = ivle.database.User.get_by_login(req.store, username)
131
130
        if user is None:
136
135
        except ValueError:
137
136
            raise NotFound()
138
137
 
139
 
        worksheet_exercise = req.store.find(WorksheetExercise,
140
 
            WorksheetExercise.exercise_id == exercise,
141
 
            WorksheetExercise.worksheet_id == Worksheet.id,
142
 
            Worksheet.identifier == worksheet,
 
138
        exercise = ivle.database.Exercise.get_by_name(req.store, exercise)
 
139
        worksheet = req.store.find(Worksheet,
 
140
            Worksheet.name == self.worksheet,
143
141
            Worksheet.offering_id == Offering.id,
144
142
            Offering.subject_id == Subject.id,
145
 
            Subject.code == subject,
 
143
            Subject.code == self.subject,
146
144
            Offering.semester_id == Semester.id,
147
 
            Semester.year == year,
148
 
            Semester.semester == semester).one()
149
 
            
 
145
            Semester.year == self.year,
 
146
            Semester.semester == self.semester).one()
 
147
 
150
148
        attempt = ivle.worksheet.get_exercise_attempt(req.store, user,
151
 
                        worksheet_exercise, as_of=date,
152
 
                        allow_inactive=HISTORY_ALLOW_INACTIVE) 
 
149
            exercise, worksheet, as_of=date, allow_inactive=HISTORY_ALLOW_INACTIVE)
153
150
 
154
151
        if attempt is None:
155
152
            raise NotFound()
166
163
 
167
164
    def get_permissions(self, user):
168
165
        # XXX: Do it properly.
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?
171
166
        if user is not None:
172
167
            return set(['save'])
173
168
        else:
175
170
 
176
171
    @named_operation('save')
177
172
    def save(self, req, text):
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,
181
 
            WorksheetExercise.exercise_id == self.exercise,
182
 
            WorksheetExercise.worksheet_id == Worksheet.id,
 
173
        # Need to open JUST so we know this is a real exercise.
 
174
        # (This avoids users submitting code for bogus exercises).
 
175
        exercise = req.store.find(Exercise,
 
176
            Exercise.id == self.exercise).one()
 
177
        worksheet = req.store.find(Worksheet,
 
178
            Worksheet.name == self.worksheet,
183
179
            Worksheet.offering_id == Offering.id,
184
180
            Offering.subject_id == Subject.id,
185
181
            Subject.code == self.subject,
186
182
            Offering.semester_id == Semester.id,
187
183
            Semester.year == self.year,
188
184
            Semester.semester == self.semester).one()
189
 
        
190
 
        if worksheet_exercise is None:
191
 
            raise NotFound()
192
 
 
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
 
        
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()
208
 
 
 
185
        ivle.worksheet.save_exercise(req.store, req.user, exercise, worksheet,
 
186
                                     unicode(text), datetime.datetime.now())
209
187
        return {"result": "ok"}
210
188
 
211
 
 
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)
261
189
class WorksheetRESTView(JSONRESTView):
262
190
    """View used to update a worksheet."""
263
191
 
 
192
    def generate_exerciselist(self, req, worksheet):
 
193
        """Runs through the worksheetstream, generating the appropriate
 
194
        WorksheetExercises, and de-activating the old ones."""
 
195
        exercises = []
 
196
        # Turns the worksheet into an xml stream, and then finds all the 
 
197
        # exercise nodes in the stream.
 
198
        worksheet = genshi.XML(worksheet)
 
199
        for kind, data, pos in worksheet:
 
200
            if kind is genshi.core.START:
 
201
                # Data is a tuple of tag name and a list of name->value tuples
 
202
                if data[0] == 'exercise':
 
203
                    src = ""
 
204
                    optional = False
 
205
                    for attr in data[1]:
 
206
                        if attr[0] == 'src':
 
207
                            src = attr[1]
 
208
                        if attr[0] == 'optional':
 
209
                            optional = attr[1] == 'true'
 
210
                    if src != "":
 
211
                        exercises.append((src, optional))
 
212
        ex_num = 0
 
213
        # Set all current worksheet_exercises to be inactive
 
214
        db_worksheet_exercises = req.store.find(WorksheetExercise,
 
215
            WorksheetExercise.worksheet_id == self.context.id)
 
216
        for worksheet_exercise in db_worksheet_exercises:
 
217
            worksheet_exercise.active = False
 
218
        
 
219
        for exerciseid, optional in exercises:
 
220
            worksheet_exercise = req.store.find(WorksheetExercise,
 
221
                WorksheetExercise.worksheet_id == self.context.id,
 
222
                Exercise.id == WorksheetExercise.exercise_id,
 
223
                Exercise.id == exerciseid).one()
 
224
            if worksheet_exercise is None:
 
225
                exercise = req.store.find(Exercise,
 
226
                    Exercise.id == exerciseid
 
227
                ).one()
 
228
                if exercise is None:
 
229
                    raise NotFound()
 
230
                worksheet_exercise = WorksheetExercise()
 
231
                worksheet_exercise.worksheet_id = self.context.id
 
232
                worksheet_exercise.exercise_id = exercise.id
 
233
                req.store.add(worksheet_exercise)
 
234
            worksheet_exercise.active = True
 
235
            worksheet_exercise.seq_no = ex_num
 
236
            worksheet_exercise.optional = optional
 
237
 
264
238
    def get_permissions(self, user):
265
239
        # XXX: Do it properly.
266
 
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
267
 
        #      under their control
268
240
        if user is not None:
269
241
            if user.rolenm == 'admin':
270
242
                return set(['save'])
293
265
            raise NotFound()
294
266
    
295
267
    @named_operation('save')
296
 
    def save(self, req, name, assessable, data, format):
 
268
    def save(self, req, name, assessable, data):
297
269
        """Takes worksheet data and saves it."""
298
 
        generate_exerciselist(self.context, req, data)
 
270
        self.generate_exerciselist(req, data)
299
271
        
300
272
        self.context.name = unicode(name)
 
273
        self.context.data = unicode(data)
301
274
        self.context.assessable = self.convert_bool(assessable)
302
 
        self.context.data = unicode(data)
303
 
        self.context.format = unicode(format)
304
 
        
305
 
        return {"result": "ok"}
306
 
 
307
 
class WorksheetsRESTView(JSONRESTView):
308
 
    """View used to update and create 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':
316
 
                return set(['edit'])
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
 
 
338
 
    @named_operation('edit')
339
 
    def add_worksheet(self, req, identifier, name, assessable, data, format):
340
 
        """Takes worksheet data and adds it."""
341
 
        
342
 
        new_worksheet = Worksheet()
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
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)
352
 
        
353
 
        # This call is added for clarity, as the worksheet is implicitly added.        
354
 
        req.store.add(new_worksheet)
355
 
        
356
 
        generate_exerciselist(new_worksheet, req, data)
357
 
        
358
 
        return {"result": "ok"}
359
 
 
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
399
 
        
400
 
        return {'result': 'ok'}
 
275
        
 
276
        return {"result": "ok"}