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

« back to all changes in this revision

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

  • Committer: Matt Giuca
  • Date: 2010-07-21 04:12:30 UTC
  • Revision ID: matt.giuca@gmail.com-20100721041230-hlrn2q3fx2uvaprd
ivle.webapp.admin.user: Fixed call to req.user.get_svn_url (now takes 1 argument, as of r1810).
Fixes Internal server error on Settings page (since r1810).

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
'''AJAX backend for the tutorial application.'''
21
21
 
22
 
import os
23
22
import datetime
 
23
 
24
24
import genshi
 
25
from storm.locals import Store
25
26
 
26
 
import ivle.util
27
 
import ivle.console
28
27
import ivle.database
29
28
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
30
 
                          Offering, Subject, Semester, WorksheetExercise
31
 
import ivle.worksheet
32
 
import ivle.conf
33
 
import ivle.webapp.tutorial.test
34
 
 
35
 
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
 
29
                          Offering, Subject, Semester, User, WorksheetExercise
 
30
import ivle.worksheet.utils
 
31
from ivle.webapp.base.rest import (JSONRESTView, write_operation,
36
32
                                   require_permission)
37
33
from ivle.webapp.errors import NotFound
38
34
 
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
35
 
43
36
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
44
37
 
 
38
 
45
39
class AttemptsRESTView(JSONRESTView):
46
40
    '''REST view of a user's attempts at an exercise.'''
47
 
    def __init__(self, req, subject, year, semester, worksheet, 
48
 
                                                exercise, username):
49
 
        self.user = ivle.database.User.get_by_login(req.store, username)
50
 
        if self.user is None:
51
 
            raise NotFound()
52
 
        
53
 
        self.worksheet_exercise = req.store.find(WorksheetExercise,
54
 
            WorksheetExercise.exercise_id == exercise,
55
 
            WorksheetExercise.worksheet_id == Worksheet.id,
56
 
            Worksheet.offering_id == Offering.id,
57
 
            Offering.subject_id == Subject.id,
58
 
            Subject.code == subject,
59
 
            Offering.semester_id == Semester.id,
60
 
            Semester.year == year,
61
 
            Semester.semester == semester).one()
62
 
        
63
 
        self.context = self.user # XXX: Not quite right.
64
41
 
65
42
    @require_permission('edit')
66
43
    def GET(self, req):
67
44
        """Handles a GET Attempts action."""
68
45
        attempts = req.store.find(ExerciseAttempt, 
69
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
70
 
                ExerciseAttempt.user_id == self.user.id)
 
46
                ExerciseAttempt.ws_ex_id == self.context.worksheet_exercise.id,
 
47
                ExerciseAttempt.user_id == self.context.user.id)
71
48
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
72
49
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
73
50
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
79
56
    @require_permission('edit')
80
57
    def PUT(self, req, data):
81
58
        """ Tests the given submission """
82
 
        exercise = req.store.find(Exercise, 
83
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
84
 
        if exercise is None:
85
 
            raise NotFound()
86
 
 
87
 
        # Start a console to run the tests on
88
 
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
89
 
        working_dir = os.path.join("/home", req.user.login)
90
 
        cons = ivle.console.Console(req.user.unixid, jail_path, working_dir)
91
 
 
92
 
        # Parse the file into a exercise object using the test suite
93
 
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
94
 
                                                            exercise, cons)
95
 
 
96
 
        # Run the test cases. Get the result back as a JSONable object.
97
 
        # Return it.
98
 
        test_results = exercise_obj.run_tests(data['code'])
99
 
 
100
 
        # Close the console
101
 
        cons.close()
 
59
        test_results = ivle.worksheet.utils.test_exercise_submission(
 
60
            req.config, req.user, self.context.worksheet_exercise.exercise,
 
61
            data['code'])
102
62
 
103
63
        attempt = ivle.database.ExerciseAttempt(user=req.user,
104
 
            worksheet_exercise = self.worksheet_exercise,
 
64
            worksheet_exercise = self.context.worksheet_exercise,
105
65
            date = datetime.datetime.now(),
106
66
            complete = test_results['passed'],
107
67
            text = unicode(data['code'])
112
72
        # Query the DB to get an updated score on whether or not this problem
113
73
        # has EVER been completed (may be different from "passed", if it has
114
74
        # 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)
 
75
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
 
76
                req.store, req.user, self.context.worksheet_exercise)
117
77
        test_results["completed"] = completed
118
78
        test_results["attempts"] = attempts
119
79
 
123
83
class AttemptRESTView(JSONRESTView):
124
84
    '''REST view of an exercise attempt.'''
125
85
 
126
 
    def __init__(self, req, subject, year, semester, worksheet, exercise, 
127
 
                 username, date):
128
 
        # TODO: Find exercise within worksheet.
129
 
        user = ivle.database.User.get_by_login(req.store, username)
130
 
        if user is None:
131
 
            raise NotFound()
132
 
 
133
 
        #try:
134
 
        #    date = datetime.datetime.strptime(date, TIMESTAMP_FORMAT)
135
 
        #except ValueError:
136
 
        #    raise NotFound()
137
 
 
138
 
        worksheet_exercise = req.store.find(WorksheetExercise,
139
 
            WorksheetExercise.exercise_id == exercise,
140
 
            WorksheetExercise.worksheet_id == Worksheet.id,
141
 
            Worksheet.identifier == worksheet,
142
 
            Worksheet.offering_id == Offering.id,
143
 
            Offering.subject_id == Subject.id,
144
 
            Subject.code == subject,
145
 
            Offering.semester_id == Semester.id,
146
 
            Semester.year == year,
147
 
            Semester.semester == semester).one()
148
 
            
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()
154
 
 
155
 
        if attempt is None:
156
 
            raise NotFound()
157
 
 
158
 
        self.context = attempt
159
 
 
160
86
    @require_permission('view')
161
87
    def GET(self, req):
162
88
        return {'code': self.context.text}
163
89
 
164
90
 
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')
 
91
class WorksheetExerciseRESTView(JSONRESTView):
 
92
    '''REST view of a worksheet exercise.'''
 
93
 
 
94
    @write_operation('view')
178
95
    def save(self, req, text):
179
96
        # Find the appropriate WorksheetExercise to save to. If its not found,
180
97
        # 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
98
 
194
99
        old_save = req.store.find(ExerciseSave,
195
 
            ExerciseSave.ws_ex_id == worksheet_exercise.id,
 
100
            ExerciseSave.ws_ex_id == self.context.id,
196
101
            ExerciseSave.user == req.user).one()
197
102
        
198
103
        #Overwrite the old, or create a new if there isn't one
202
107
        else:
203
108
            new_save = old_save
204
109
        
205
 
        new_save.worksheet_exercise = worksheet_exercise
 
110
        new_save.worksheet_exercise = self.context
206
111
        new_save.user = req.user
207
112
        new_save.text = unicode(text)
208
113
        new_save.date = datetime.datetime.now()
210
115
        return {"result": "ok"}
211
116
 
212
117
 
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
 
# Note that this is the view of an existing worksheet. Creation is handled
261
 
# by OfferingRESTView (as offerings have worksheets)
262
 
class WorksheetRESTView(JSONRESTView):
263
 
    """View used to update a worksheet."""
264
 
 
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
 
    def __init__(self, req, **kwargs):
278
 
    
279
 
        self.worksheet = kwargs['worksheet']
280
 
        self.subject = kwargs['subject']
281
 
        self.year = kwargs['year']
282
 
        self.semester = kwargs['semester']
283
 
    
284
 
        self.context = req.store.find(Worksheet,
285
 
            Worksheet.identifier == self.worksheet,
286
 
            Worksheet.offering_id == Offering.id,
287
 
            Offering.subject_id == Subject.id,
288
 
            Subject.code == self.subject,
289
 
            Offering.semester_id == Semester.id,
290
 
            Semester.year == self.year,
291
 
            Semester.semester == self.semester).one()
292
 
        
293
 
        if self.context is None:
294
 
            raise NotFound()
295
 
    
296
 
    @named_operation('save')
297
 
    def save(self, req, name, assessable, data, format):
298
 
        """Takes worksheet data and saves it."""
299
 
        self.generate_exerciselist(self.context, req, data)
300
 
        
301
 
        self.context.name = unicode(name)
302
 
        self.context.assessable = self.convert_bool(assessable)
303
 
        self.context.data = unicode(data)
304
 
        
305
 
        return {"result": "ok"}
306
 
 
307
118
class WorksheetsRESTView(JSONRESTView):
308
119
    """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(['add_worksheet', 'set_sequence'])
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('add_worksheet')
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('set_sequence')
361
 
    def seq_sequence(self, req, worksheet_list):
362
 
        """Takes a list of worksheet-seq_no pairs and updates their 
363
 
        corresponding Worksheet objects to match."""
364
 
        
365
 
        for worksheetid, seq_no in worksheet_list:
366
 
            worksheet = req.store.find(Worksheet,
367
 
                Worksheet.offering_id == self.context.id,
368
 
                Worksheet.identifier == worksheetid).one()
369
 
            if worksheet is None:
370
 
                raise NotFound(worksheet)
371
 
            worksheet.seq_no = seq_no
 
120
 
 
121
    @write_operation('edit_worksheets')
 
122
    def move_up(self, req, worksheetid):
 
123
        """Takes a list of worksheet-seq_no pairs and updates their 
 
124
        corresponding Worksheet objects to match."""
 
125
        
 
126
        worksheet_below = req.store.find(Worksheet,
 
127
            Worksheet.offering_id == self.context.id,
 
128
            Worksheet.identifier == unicode(worksheetid)).one()
 
129
        if worksheet_below is None:
 
130
            raise NotFound('worksheet_below')
 
131
        worksheet_above = req.store.find(Worksheet,
 
132
            Worksheet.offering_id == self.context.id,
 
133
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
 
134
        if worksheet_above is None:
 
135
            raise NotFound('worksheet_above')
 
136
 
 
137
        worksheet_below.seq_no = worksheet_below.seq_no - 1
 
138
        worksheet_above.seq_no = worksheet_above.seq_no + 1
 
139
        
 
140
        return {'result': 'ok'}
 
141
 
 
142
    @write_operation('edit_worksheets')
 
143
    def move_down(self, req, worksheetid):
 
144
        """Takes a list of worksheet-seq_no pairs and updates their 
 
145
        corresponding Worksheet objects to match."""
 
146
        
 
147
        worksheet_above = req.store.find(Worksheet,
 
148
            Worksheet.offering_id == self.context.id,
 
149
            Worksheet.identifier == unicode(worksheetid)).one()
 
150
        if worksheet_above is None:
 
151
            raise NotFound('worksheet_below')
 
152
        worksheet_below = req.store.find(Worksheet,
 
153
            Worksheet.offering_id == self.context.id,
 
154
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
 
155
        if worksheet_below is None:
 
156
            raise NotFound('worksheet_above')
 
157
 
 
158
        worksheet_below.seq_no = worksheet_below.seq_no - 1
 
159
        worksheet_above.seq_no = worksheet_above.seq_no + 1
372
160
        
373
161
        return {'result': 'ok'}