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

« back to all changes in this revision

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

Direct port of userservice to the new framework.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
import os
23
23
import datetime
24
 
import genshi
25
24
 
26
25
import ivle.util
27
26
import ivle.console
28
27
import ivle.database
29
 
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
30
 
                          Offering, Subject, Semester, WorksheetExercise
31
28
import ivle.worksheet
32
29
import ivle.conf
33
30
import ivle.webapp.tutorial.test
42
39
 
43
40
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
44
41
 
 
42
 
45
43
class AttemptsRESTView(JSONRESTView):
46
44
    '''REST view of a user's attempts at an exercise.'''
47
 
    
48
 
    def __init__(self, req, subject, year, semester, worksheet, 
49
 
                                                exercise, username):
 
45
    def __init__(self, req, subject, worksheet, exercise, username):
 
46
        # TODO: Find exercise within worksheet.
50
47
        self.user = ivle.database.User.get_by_login(req.store, username)
51
48
        if self.user is None:
52
49
            raise NotFound()
53
 
        
54
 
        self.worksheet_exercise = req.store.find(WorksheetExercise,
55
 
            WorksheetExercise.exercise_id == exercise,
56
 
            WorksheetExercise.worksheet_id == Worksheet.id,
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
 
        
 
50
        self.exercise = exercise
64
51
        self.context = self.user # XXX: Not quite right.
65
52
 
66
53
    @require_permission('edit')
67
54
    def GET(self, req):
68
55
        """Handles a GET Attempts action."""
69
 
        attempts = req.store.find(ExerciseAttempt, 
70
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
71
 
                ExerciseAttempt.user_id == self.user.id)
 
56
        exercise = ivle.database.Exercise.get_by_name(req.store, 
 
57
                                                        self.exercise)
 
58
 
 
59
        attempts = ivle.worksheet.get_exercise_attempts(req.store, self.user,
 
60
                            exercise, allow_inactive=HISTORY_ALLOW_INACTIVE)
72
61
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
73
62
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
74
63
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
80
69
    @require_permission('edit')
81
70
    def PUT(self, req, data):
82
71
        """ Tests the given submission """
83
 
        exercise = req.store.find(Exercise, 
84
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
85
 
        if exercise is None:
 
72
        exercisefile = ivle.util.open_exercise_file(self.exercise)
 
73
        if exercisefile is None:
86
74
            raise NotFound()
87
75
 
88
76
        # Start a console to run the tests on
92
80
 
93
81
        # Parse the file into a exercise object using the test suite
94
82
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
95
 
                                                            exercise, cons)
 
83
                                                            exercisefile, cons)
 
84
        exercisefile.close()
96
85
 
97
86
        # Run the test cases. Get the result back as a JSONable object.
98
87
        # Return it.
101
90
        # Close the console
102
91
        cons.close()
103
92
 
 
93
        # Get the Exercise from the database
 
94
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
95
 
104
96
        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
 
        )
 
97
                                                exercise=exercise,
 
98
                                                date=datetime.datetime.now(),
 
99
                                                complete=test_results['passed'],
 
100
                                                # XXX
 
101
                                                text=unicode(data['code']))
110
102
 
111
103
        req.store.add(attempt)
112
104
 
114
106
        # has EVER been completed (may be different from "passed", if it has
115
107
        # been completed before), and the total number of attempts.
116
108
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
117
 
            req.user, self.worksheet_exercise)
 
109
            req.user, exercise)
118
110
        test_results["completed"] = completed
119
111
        test_results["attempts"] = attempts
120
112
 
124
116
class AttemptRESTView(JSONRESTView):
125
117
    '''REST view of an exercise attempt.'''
126
118
 
127
 
    def __init__(self, req, subject, year, semester, worksheet, exercise, 
128
 
                 username, date):
 
119
    def __init__(self, req, subject, worksheet, exercise, username, date):
129
120
        # TODO: Find exercise within worksheet.
130
121
        user = ivle.database.User.get_by_login(req.store, username)
131
122
        if user is None:
136
127
        except ValueError:
137
128
            raise NotFound()
138
129
 
139
 
        worksheet_exercise = req.store.find(WorksheetExercise,
140
 
            WorksheetExercise.exercise_id == exercise,
141
 
            WorksheetExercise.worksheet_id == Worksheet.id,
142
 
            Worksheet.identifier == worksheet,
143
 
            Worksheet.offering_id == Offering.id,
144
 
            Offering.subject_id == Subject.id,
145
 
            Subject.code == subject,
146
 
            Offering.semester_id == Semester.id,
147
 
            Semester.year == year,
148
 
            Semester.semester == semester).one()
149
 
            
 
130
        exercise = ivle.database.Exercise.get_by_name(req.store, exercise)
150
131
        attempt = ivle.worksheet.get_exercise_attempt(req.store, user,
151
 
                        worksheet_exercise, as_of=date,
152
 
                        allow_inactive=HISTORY_ALLOW_INACTIVE) 
 
132
            exercise, as_of=date, allow_inactive=HISTORY_ALLOW_INACTIVE)
153
133
 
154
134
        if attempt is None:
155
135
            raise NotFound()
166
146
 
167
147
    def get_permissions(self, user):
168
148
        # 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
149
        if user is not None:
172
150
            return set(['save'])
173
151
        else:
175
153
 
176
154
    @named_operation('save')
177
155
    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,
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()
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
 
 
209
 
        return {"result": "ok"}
210
 
 
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
 
class WorksheetRESTView(JSONRESTView):
262
 
    """View used to update a worksheet."""
263
 
 
264
 
    def get_permissions(self, user):
265
 
        # XXX: Do it properly.
266
 
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
267
 
        #      under their control
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,
284
 
            Worksheet.identifier == self.worksheet,
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()
291
 
        
292
 
        if self.context is None:
293
 
            raise NotFound()
294
 
    
295
 
    @named_operation('save')
296
 
    def save(self, req, name, assessable, data, format):
297
 
        """Takes worksheet data and saves it."""
298
 
        generate_exerciselist(self.context, req, data)
299
 
        
300
 
        self.context.name = unicode(name)
301
 
        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'}
 
156
        # Need to open JUST so we know this is a real exercise.
 
157
        # (This avoids users submitting code for bogus exercises).
 
158
        exercisefile = ivle.util.open_exercise_file(self.exercise)
 
159
        if exercisefile is None:
 
160
            raise NotFound()
 
161
        exercisefile.close()
 
162
 
 
163
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
164
        ivle.worksheet.save_exercise(req.store, req.user, exercise,
 
165
                                     unicode(text), datetime.datetime.now())
 
166
        return {"result": "ok"}