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

« back to all changes in this revision

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

  • Committer: William Grant
  • Date: 2009-04-28 07:08:56 UTC
  • Revision ID: grantw@unimelb.edu.au-20090428070856-75yc00g6ea24qfqz
Drop ivle.conf.{subjects,exercises}_base - they were unused.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
import datetime
24
24
import genshi
25
25
 
26
 
import ivle.util
27
26
import ivle.console
28
27
import ivle.database
29
28
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
30
29
                          Offering, Subject, Semester, WorksheetExercise
31
 
import ivle.worksheet
 
30
import ivle.worksheet.utils
32
31
import ivle.conf
33
32
import ivle.webapp.tutorial.test
34
 
 
35
33
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
36
34
                                   require_permission)
37
35
from ivle.webapp.errors import NotFound
44
42
 
45
43
class AttemptsRESTView(JSONRESTView):
46
44
    '''REST view of a user's attempts at an exercise.'''
 
45
    
47
46
    def __init__(self, req, subject, year, semester, worksheet, 
48
47
                                                exercise, username):
49
48
        self.user = ivle.database.User.get_by_login(req.store, username)
51
50
            raise NotFound()
52
51
        
53
52
        self.worksheet_exercise = req.store.find(WorksheetExercise,
54
 
            WorksheetExercise.exercise_id == exercise,
 
53
            WorksheetExercise.exercise_id == unicode(exercise),
55
54
            WorksheetExercise.worksheet_id == Worksheet.id,
56
55
            Worksheet.offering_id == Offering.id,
 
56
            Worksheet.identifier == unicode(worksheet),
57
57
            Offering.subject_id == Subject.id,
58
 
            Subject.code == subject,
 
58
            Subject.short_name == subject,
59
59
            Offering.semester_id == Semester.id,
60
60
            Semester.year == year,
61
61
            Semester.semester == semester).one()
112
112
        # Query the DB to get an updated score on whether or not this problem
113
113
        # has EVER been completed (may be different from "passed", if it has
114
114
        # been completed before), and the total number of attempts.
115
 
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
116
 
            req.user, self.worksheet_exercise)
 
115
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
 
116
                req.store, req.user, self.worksheet_exercise)
117
117
        test_results["completed"] = completed
118
118
        test_results["attempts"] = attempts
119
119
 
130
130
        if user is None:
131
131
            raise NotFound()
132
132
 
133
 
        #try:
134
 
        #    date = datetime.datetime.strptime(date, TIMESTAMP_FORMAT)
135
 
        #except ValueError:
136
 
        #    raise NotFound()
 
133
        try:
 
134
            date = datetime.datetime.strptime(date, TIMESTAMP_FORMAT)
 
135
        except ValueError:
 
136
            raise NotFound()
 
137
 
 
138
        # XXX Hack around Google Code issue #87
 
139
        # Query from the given date +1 secnod.
 
140
        # Date is in seconds (eg. 3:47:12), while the data is in finer time
 
141
        # (eg. 3:47:12.3625). The query "date <= 3:47:12" will fail because
 
142
        # 3:47:12.3625 is greater. Hence we do the query from +1 second,
 
143
        # "date <= 3:47:13", and it finds the correct submission, UNLESS there
 
144
        # are multiple submissions inside the same second.
 
145
        date += datetime.timedelta(seconds=1)
137
146
 
138
147
        worksheet_exercise = req.store.find(WorksheetExercise,
139
148
            WorksheetExercise.exercise_id == exercise,
141
150
            Worksheet.identifier == worksheet,
142
151
            Worksheet.offering_id == Offering.id,
143
152
            Offering.subject_id == Subject.id,
144
 
            Subject.code == subject,
 
153
            Subject.short_name == subject,
145
154
            Offering.semester_id == Semester.id,
146
155
            Semester.year == year,
147
156
            Semester.semester == semester).one()
148
157
            
149
 
        attempt = req.store.find(ExerciseAttempt,
150
 
            ExerciseAttempt.user_id == user.id,
151
 
            ExerciseAttempt.ws_ex_id == worksheet_exercise.id,
152
 
            ExerciseAttempt.date == date
153
 
        ).one()
 
158
        attempt = ivle.worksheet.utils.get_exercise_attempt(req.store, user,
 
159
                        worksheet_exercise, as_of=date,
 
160
                        allow_inactive=HISTORY_ALLOW_INACTIVE) 
154
161
 
155
162
        if attempt is None:
156
163
            raise NotFound()
162
169
        return {'code': self.context.text}
163
170
 
164
171
 
165
 
class ExerciseRESTView(JSONRESTView):
166
 
    '''REST view of an exercise.'''
167
 
 
168
 
    def get_permissions(self, user):
169
 
        # XXX: Do it properly.
170
 
        # XXX: Does any user have the ability to save as themselves?
171
 
        # XXX: Does a user EVER have permission to save as another user?
172
 
        if user is not None:
173
 
            return set(['save'])
174
 
        else:
175
 
            return set()
176
 
 
177
 
    @named_operation('save')
 
172
class WorksheetExerciseRESTView(JSONRESTView):
 
173
    '''REST view of a worksheet exercise.'''
 
174
 
 
175
    def __init__(self, req, subject, year, semester, worksheet, exercise):
 
176
        self.context = req.store.find(WorksheetExercise,
 
177
            WorksheetExercise.exercise_id == exercise,
 
178
            WorksheetExercise.worksheet_id == Worksheet.id,
 
179
            Worksheet.offering_id == Offering.id,
 
180
            Offering.subject_id == Subject.id,
 
181
            Subject.short_name == subject,
 
182
            Offering.semester_id == Semester.id,
 
183
            Semester.year == year,
 
184
            Semester.semester == semester).one()
 
185
        
 
186
        if self.context is None:
 
187
            raise NotFound()
 
188
 
 
189
    @named_operation('view')
178
190
    def save(self, req, text):
179
191
        # Find the appropriate WorksheetExercise to save to. If its not found,
180
192
        # the user is submitting against a non-existant worksheet/exercise
181
 
        worksheet_exercise = req.store.find(WorksheetExercise,
182
 
            WorksheetExercise.exercise_id == self.exercise,
183
 
            WorksheetExercise.worksheet_id == Worksheet.id,
184
 
            Worksheet.offering_id == Offering.id,
185
 
            Offering.subject_id == Subject.id,
186
 
            Subject.code == self.subject,
187
 
            Offering.semester_id == Semester.id,
188
 
            Semester.year == self.year,
189
 
            Semester.semester == self.semester).one()
190
 
        
191
 
        if worksheet_exercise is None:
192
 
            raise NotFound()
193
193
 
194
194
        old_save = req.store.find(ExerciseSave,
195
 
            ExerciseSave.ws_ex_id == worksheet_exercise.id,
 
195
            ExerciseSave.ws_ex_id == self.context.id,
196
196
            ExerciseSave.user == req.user).one()
197
197
        
198
198
        #Overwrite the old, or create a new if there isn't one
202
202
        else:
203
203
            new_save = old_save
204
204
        
205
 
        new_save.worksheet_exercise = worksheet_exercise
 
205
        new_save.worksheet_exercise = self.context
206
206
        new_save.user = req.user
207
207
        new_save.text = unicode(text)
208
208
        new_save.date = datetime.datetime.now()
210
210
        return {"result": "ok"}
211
211
 
212
212
 
213
 
def generate_exerciselist(worksheet, req, worksheetdata):
214
 
    """Runs through the worksheetstream, generating the appropriate
215
 
    WorksheetExercises, and de-activating the old ones."""
216
 
    exercises = []
217
 
    # Turns the worksheet into an xml stream, and then finds all the 
218
 
    # exercise nodes in the stream.
219
 
    worksheetdata = genshi.XML(worksheetdata)
220
 
    for kind, data, pos in worksheetdata:
221
 
        if kind is genshi.core.START:
222
 
            # Data is a tuple of tag name and a list of name->value tuples
223
 
            if data[0] == 'exercise':
224
 
                src = ""
225
 
                optional = False
226
 
                for attr in data[1]:
227
 
                    if attr[0] == 'src':
228
 
                        src = attr[1]
229
 
                    if attr[0] == 'optional':
230
 
                        optional = attr[1] == 'true'
231
 
                if src != "":
232
 
                    exercises.append((src, optional))
233
 
    ex_num = 0
234
 
    # Set all current worksheet_exercises to be inactive
235
 
    db_worksheet_exercises = req.store.find(WorksheetExercise,
236
 
        WorksheetExercise.worksheet_id == worksheet.id)
237
 
    for worksheet_exercise in db_worksheet_exercises:
238
 
        worksheet_exercise.active = False
239
 
    
240
 
    for exerciseid, optional in exercises:
241
 
        worksheet_exercise = req.store.find(WorksheetExercise,
242
 
            WorksheetExercise.worksheet_id == worksheet.id,
243
 
            Exercise.id == WorksheetExercise.exercise_id,
244
 
            Exercise.id == exerciseid).one()
245
 
        if worksheet_exercise is None:
246
 
            exercise = req.store.find(Exercise,
247
 
                Exercise.id == exerciseid
248
 
            ).one()
249
 
            if exercise is None:
250
 
                raise NotFound()
251
 
            worksheet_exercise = WorksheetExercise()
252
 
            worksheet_exercise.worksheet_id = worksheet.id
253
 
            worksheet_exercise.exercise_id = exercise.id
254
 
            req.store.add(worksheet_exercise)
255
 
        worksheet_exercise.active = True
256
 
        worksheet_exercise.seq_no = ex_num
257
 
        worksheet_exercise.optional = optional
258
 
 
259
 
 
260
213
# Note that this is the view of an existing worksheet. Creation is handled
261
214
# by OfferingRESTView (as offerings have worksheets)
262
215
class WorksheetRESTView(JSONRESTView):
263
216
    """View used to update a worksheet."""
264
217
 
265
 
    def get_permissions(self, user):
266
 
        # XXX: Do it properly.
267
 
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
268
 
        #      under their control
269
 
        if user is not None:
270
 
            if user.rolenm == 'admin':
271
 
                return set(['save'])
272
 
            else:
273
 
                return set()
274
 
        else:
275
 
            return set()    
276
 
 
277
218
    def __init__(self, req, **kwargs):
278
219
    
279
220
        self.worksheet = kwargs['worksheet']
285
226
            Worksheet.identifier == self.worksheet,
286
227
            Worksheet.offering_id == Offering.id,
287
228
            Offering.subject_id == Subject.id,
288
 
            Subject.code == self.subject,
 
229
            Subject.short_name == self.subject,
289
230
            Offering.semester_id == Semester.id,
290
231
            Semester.year == self.year,
291
232
            Semester.semester == self.semester).one()
293
234
        if self.context is None:
294
235
            raise NotFound()
295
236
    
296
 
    @named_operation('save')
 
237
    @named_operation('edit')
297
238
    def save(self, req, name, assessable, data, format):
298
239
        """Takes worksheet data and saves it."""
299
 
        self.generate_exerciselist(self.context, req, data)
300
 
        
301
240
        self.context.name = unicode(name)
302
241
        self.context.assessable = self.convert_bool(assessable)
303
242
        self.context.data = unicode(data)
 
243
        self.context.format = unicode(format)
 
244
        ivle.worksheet.utils.update_exerciselist(self.context)
304
245
        
305
246
        return {"result": "ok"}
306
247
 
307
 
class OfferingRESTView(JSONRESTView):
308
 
    """View used to update Offering and create Worksheets."""
 
248
class WorksheetsRESTView(JSONRESTView):
 
249
    """View used to update and create Worksheets."""
309
250
    
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(['add_worksheet'])
317
 
            else:
318
 
                return set()
319
 
        else:
320
 
            return set()
321
 
 
322
251
    def __init__(self, req, **kwargs):
323
252
    
324
253
        self.subject = kwargs['subject']
327
256
    
328
257
        self.context = req.store.find(Offering,
329
258
            Offering.subject_id == Subject.id,
330
 
            Subject.code == self.subject,
 
259
            Subject.short_name == self.subject,
331
260
            Offering.semester_id == Semester.id,
332
261
            Semester.year == self.year,
333
262
            Semester.semester == self.semester).one()
335
264
        if self.context is None:
336
265
            raise NotFound()
337
266
 
338
 
    @named_operation('add_worksheet')
339
 
    def add_worksheet(self,req, identifier, name, assessable, data, format):
 
267
    @named_operation('edit')
 
268
    def add_worksheet(self, req, identifier, name, assessable, data, format):
340
269
        """Takes worksheet data and adds it."""
341
270
        
342
271
        new_worksheet = Worksheet()
343
 
        
 
272
        new_worksheet.seq_no = self.context.worksheets.count()
 
273
        # Setting new_worksheet.offering implicitly adds new_worksheet,
 
274
        # hence worksheets.count MUST be called above it
344
275
        new_worksheet.offering = self.context
345
276
        new_worksheet.identifier = unicode(identifier)
346
277
        new_worksheet.name = unicode(name)
347
278
        new_worksheet.assessable = self.convert_bool(assessable)
348
279
        new_worksheet.data = unicode(data)
349
280
        new_worksheet.format = unicode(format)
350
 
        new_worksheet.seq_no = self.context.worksheets.count()
 
281
        
 
282
        # This call is added for clarity, as the worksheet is implicitly added.        
351
283
        req.store.add(new_worksheet)
352
 
        
353
 
        generate_exerciselist(new_worksheet, req, data)
354
 
        
 
284
 
 
285
        ivle.worksheet.utils.update_exerciselist(new_worksheet)
 
286
 
355
287
        return {"result": "ok"}
356
288
 
 
289
    @named_operation('edit')
 
290
    def move_up(self, req, worksheetid):
 
291
        """Takes a list of worksheet-seq_no pairs and updates their 
 
292
        corresponding Worksheet objects to match."""
 
293
        
 
294
        worksheet_below = req.store.find(Worksheet,
 
295
            Worksheet.offering_id == self.context.id,
 
296
            Worksheet.identifier == unicode(worksheetid)).one()
 
297
        if worksheet_below is None:
 
298
            raise NotFound('worksheet_below')
 
299
        worksheet_above = req.store.find(Worksheet,
 
300
            Worksheet.offering_id == self.context.id,
 
301
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
 
302
        if worksheet_above is None:
 
303
            raise NotFound('worksheet_above')
 
304
 
 
305
        worksheet_below.seq_no = worksheet_below.seq_no - 1
 
306
        worksheet_above.seq_no = worksheet_above.seq_no + 1
 
307
        
 
308
        return {'result': 'ok'}
 
309
 
 
310
    @named_operation('edit')
 
311
    def move_down(self, req, worksheetid):
 
312
        """Takes a list of worksheet-seq_no pairs and updates their 
 
313
        corresponding Worksheet objects to match."""
 
314
        
 
315
        worksheet_above = req.store.find(Worksheet,
 
316
            Worksheet.offering_id == self.context.id,
 
317
            Worksheet.identifier == unicode(worksheetid)).one()
 
318
        if worksheet_above is None:
 
319
            raise NotFound('worksheet_below')
 
320
        worksheet_below = req.store.find(Worksheet,
 
321
            Worksheet.offering_id == self.context.id,
 
322
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
 
323
        if worksheet_below is None:
 
324
            raise NotFound('worksheet_above')
 
325
 
 
326
        worksheet_below.seq_no = worksheet_below.seq_no - 1
 
327
        worksheet_above.seq_no = worksheet_above.seq_no + 1
 
328
        
 
329
        return {'result': 'ok'}