~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-12-02 03:54:55 UTC
  • mto: This revision was merged to the branch mainline in revision 1353.
  • Revision ID: grantw@unimelb.edu.au-20091202035455-bk7f4t0lbn2cl4ct
Add support for public views.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
import os
23
23
import datetime
 
24
 
24
25
import genshi
 
26
from storm.locals import Store
25
27
 
26
 
import ivle.util
27
28
import ivle.console
28
29
import ivle.database
29
30
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
30
 
                          Offering, Subject, Semester, WorksheetExercise
31
 
import ivle.worksheet
32
 
import ivle.conf
 
31
                          Offering, Subject, Semester, User, WorksheetExercise
 
32
import ivle.worksheet.utils
33
33
import ivle.webapp.tutorial.test
34
 
 
35
34
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
36
35
                                   require_permission)
37
36
from ivle.webapp.errors import NotFound
38
37
 
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
38
 
43
39
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
44
40
 
45
41
class AttemptsRESTView(JSONRESTView):
46
42
    '''REST view of a user's attempts at an exercise.'''
47
 
    
48
 
    def __init__(self, req, subject, year, semester, worksheet, 
49
 
                                                exercise, username):
50
 
        self.user = ivle.database.User.get_by_login(req.store, username)
51
 
        if self.user is None:
52
 
            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
 
        
64
 
        self.context = self.user # XXX: Not quite right.
65
43
 
66
44
    @require_permission('edit')
67
45
    def GET(self, req):
68
46
        """Handles a GET Attempts action."""
69
47
        attempts = req.store.find(ExerciseAttempt, 
70
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
71
 
                ExerciseAttempt.user_id == self.user.id)
 
48
                ExerciseAttempt.ws_ex_id == self.context.worksheet_exercise.id,
 
49
                ExerciseAttempt.user_id == self.context.user.id)
72
50
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
73
51
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
74
52
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
80
58
    @require_permission('edit')
81
59
    def PUT(self, req, data):
82
60
        """ Tests the given submission """
83
 
        exercise = req.store.find(Exercise, 
84
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
85
 
        if exercise is None:
86
 
            raise NotFound()
87
 
 
88
61
        # Start a console to run the tests on
89
 
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
 
62
        jail_path = os.path.join(req.config['paths']['jails']['mounts'],
 
63
                                 req.user.login)
90
64
        working_dir = os.path.join("/home", req.user.login)
91
 
        cons = ivle.console.Console(req.user.unixid, jail_path, working_dir)
 
65
        cons = ivle.console.Console(req.config, req.user.unixid, jail_path,
 
66
                                    working_dir)
92
67
 
93
68
        # Parse the file into a exercise object using the test suite
94
69
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
95
 
                                                            exercise, cons)
 
70
                            self.context.worksheet_exercise.exercise, cons)
96
71
 
97
72
        # Run the test cases. Get the result back as a JSONable object.
98
73
        # Return it.
102
77
        cons.close()
103
78
 
104
79
        attempt = ivle.database.ExerciseAttempt(user=req.user,
105
 
            worksheet_exercise = self.worksheet_exercise,
 
80
            worksheet_exercise = self.context.worksheet_exercise,
106
81
            date = datetime.datetime.now(),
107
82
            complete = test_results['passed'],
108
83
            text = unicode(data['code'])
113
88
        # Query the DB to get an updated score on whether or not this problem
114
89
        # has EVER been completed (may be different from "passed", if it has
115
90
        # been completed before), and the total number of attempts.
116
 
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
117
 
            req.user, self.worksheet_exercise)
 
91
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
 
92
                req.store, req.user, self.context.worksheet_exercise)
118
93
        test_results["completed"] = completed
119
94
        test_results["attempts"] = attempts
120
95
 
124
99
class AttemptRESTView(JSONRESTView):
125
100
    '''REST view of an exercise attempt.'''
126
101
 
127
 
    def __init__(self, req, subject, year, semester, worksheet, exercise, 
128
 
                 username, date):
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
 
 
134
 
        #try:
135
 
        #    date = datetime.datetime.strptime(date, TIMESTAMP_FORMAT)
136
 
        #except ValueError:
137
 
        #    raise NotFound()
138
 
 
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
 
            
150
 
        attempt = req.store.find(ExerciseAttempt,
151
 
            ExerciseAttempt.user_id == user.id,
152
 
            ExerciseAttempt.ws_ex_id == worksheet_exercise.id,
153
 
            ExerciseAttempt.date == date
154
 
        ).one()
155
 
 
156
 
        if attempt is None:
157
 
            raise NotFound()
158
 
 
159
 
        self.context = attempt
160
 
 
161
102
    @require_permission('view')
162
103
    def GET(self, req):
163
104
        return {'code': self.context.text}
164
105
 
165
106
 
166
 
class ExerciseRESTView(JSONRESTView):
167
 
    '''REST view of an exercise.'''
168
 
 
169
 
    def get_permissions(self, user):
170
 
        # XXX: Do it properly.
171
 
        # XXX: Does any user have the ability to save as themselves?
172
 
        # XXX: Does a user EVER have permission to save as another user?
173
 
        if user is not None:
174
 
            return set(['save'])
175
 
        else:
176
 
            return set()
177
 
 
178
 
    @named_operation('save')
 
107
class WorksheetExerciseRESTView(JSONRESTView):
 
108
    '''REST view of a worksheet exercise.'''
 
109
 
 
110
    @named_operation('view')
179
111
    def save(self, req, text):
180
112
        # Find the appropriate WorksheetExercise to save to. If its not found,
181
113
        # the user is submitting against a non-existant worksheet/exercise
182
 
        worksheet_exercise = req.store.find(WorksheetExercise,
183
 
            WorksheetExercise.exercise_id == self.exercise,
184
 
            WorksheetExercise.worksheet_id == Worksheet.id,
185
 
            Worksheet.offering_id == Offering.id,
186
 
            Offering.subject_id == Subject.id,
187
 
            Subject.code == self.subject,
188
 
            Offering.semester_id == Semester.id,
189
 
            Semester.year == self.year,
190
 
            Semester.semester == self.semester).one()
191
 
        
192
 
        if worksheet_exercise is None:
193
 
            raise NotFound()
194
114
 
195
115
        old_save = req.store.find(ExerciseSave,
196
 
            ExerciseSave.ws_ex_id == worksheet_exercise.id,
 
116
            ExerciseSave.ws_ex_id == self.context.id,
197
117
            ExerciseSave.user == req.user).one()
198
118
        
199
119
        #Overwrite the old, or create a new if there isn't one
203
123
        else:
204
124
            new_save = old_save
205
125
        
206
 
        new_save.worksheet_exercise = worksheet_exercise
 
126
        new_save.worksheet_exercise = self.context
207
127
        new_save.user = req.user
208
128
        new_save.text = unicode(text)
209
129
        new_save.date = datetime.datetime.now()
211
131
        return {"result": "ok"}
212
132
 
213
133
 
214
 
def generate_exerciselist(worksheet, req, worksheetdata):
215
 
    """Runs through the worksheetstream, generating the appropriate
216
 
    WorksheetExercises, and de-activating the old ones."""
217
 
    exercises = []
218
 
    # Turns the worksheet into an xml stream, and then finds all the 
219
 
    # exercise nodes in the stream.
220
 
    worksheetdata = genshi.XML(worksheetdata)
221
 
    for kind, data, pos in worksheetdata:
222
 
        if kind is genshi.core.START:
223
 
            # Data is a tuple of tag name and a list of name->value tuples
224
 
            if data[0] == 'exercise':
225
 
                src = ""
226
 
                optional = False
227
 
                for attr in data[1]:
228
 
                    if attr[0] == 'src':
229
 
                        src = attr[1]
230
 
                    if attr[0] == 'optional':
231
 
                        optional = attr[1] == 'true'
232
 
                if src != "":
233
 
                    exercises.append((src, optional))
234
 
    ex_num = 0
235
 
    # Set all current worksheet_exercises to be inactive
236
 
    db_worksheet_exercises = req.store.find(WorksheetExercise,
237
 
        WorksheetExercise.worksheet_id == worksheet.id)
238
 
    for worksheet_exercise in db_worksheet_exercises:
239
 
        worksheet_exercise.active = False
240
 
    
241
 
    for exerciseid, optional in exercises:
242
 
        worksheet_exercise = req.store.find(WorksheetExercise,
243
 
            WorksheetExercise.worksheet_id == worksheet.id,
244
 
            Exercise.id == WorksheetExercise.exercise_id,
245
 
            Exercise.id == exerciseid).one()
246
 
        if worksheet_exercise is None:
247
 
            exercise = req.store.find(Exercise,
248
 
                Exercise.id == exerciseid
249
 
            ).one()
250
 
            if exercise is None:
251
 
                raise NotFound()
252
 
            worksheet_exercise = WorksheetExercise()
253
 
            worksheet_exercise.worksheet_id = worksheet.id
254
 
            worksheet_exercise.exercise_id = exercise.id
255
 
            req.store.add(worksheet_exercise)
256
 
        worksheet_exercise.active = True
257
 
        worksheet_exercise.seq_no = ex_num
258
 
        worksheet_exercise.optional = optional
259
 
 
260
 
 
261
134
# Note that this is the view of an existing worksheet. Creation is handled
262
135
# by OfferingRESTView (as offerings have worksheets)
263
136
class WorksheetRESTView(JSONRESTView):
264
137
    """View used to update a worksheet."""
265
138
 
266
 
    def get_permissions(self, user):
267
 
        # XXX: Do it properly.
268
 
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
269
 
        #      under their control
270
 
        if user is not None:
271
 
            if user.rolenm == 'admin':
272
 
                return set(['save'])
273
 
            else:
274
 
                return set()
275
 
        else:
276
 
            return set()    
277
 
 
278
 
    def __init__(self, req, **kwargs):
279
 
    
280
 
        self.worksheet = kwargs['worksheet']
281
 
        self.subject = kwargs['subject']
282
 
        self.year = kwargs['year']
283
 
        self.semester = kwargs['semester']
284
 
    
285
 
        self.context = req.store.find(Worksheet,
286
 
            Worksheet.identifier == self.worksheet,
287
 
            Worksheet.offering_id == Offering.id,
288
 
            Offering.subject_id == Subject.id,
289
 
            Subject.code == self.subject,
290
 
            Offering.semester_id == Semester.id,
291
 
            Semester.year == self.year,
292
 
            Semester.semester == self.semester).one()
293
 
        
294
 
        if self.context is None:
295
 
            raise NotFound()
296
 
    
297
 
    @named_operation('save')
 
139
    @named_operation('edit')
298
140
    def save(self, req, name, assessable, data, format):
299
141
        """Takes worksheet data and saves it."""
300
 
        self.generate_exerciselist(self.context, req, data)
301
 
        
302
142
        self.context.name = unicode(name)
303
143
        self.context.assessable = self.convert_bool(assessable)
304
144
        self.context.data = unicode(data)
 
145
        self.context.format = unicode(format)
 
146
        ivle.worksheet.utils.update_exerciselist(self.context)
305
147
        
306
148
        return {"result": "ok"}
307
149
 
308
150
class WorksheetsRESTView(JSONRESTView):
309
151
    """View used to update and create Worksheets."""
310
 
    
311
 
    def get_permissions(self, user):
312
 
        # XXX: Do it properly.
313
 
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
314
 
        #      under their control
315
 
        if user is not None:
316
 
            if user.rolenm == 'admin':
317
 
                return set(['add_worksheet', 'set_sequence'])
318
 
            else:
319
 
                return set()
320
 
        else:
321
 
            return set()
322
 
 
323
 
    def __init__(self, req, **kwargs):
324
 
    
325
 
        self.subject = kwargs['subject']
326
 
        self.year = kwargs['year']
327
 
        self.semester = kwargs['semester']
328
 
    
329
 
        self.context = req.store.find(Offering,
330
 
            Offering.subject_id == Subject.id,
331
 
            Subject.code == self.subject,
332
 
            Offering.semester_id == Semester.id,
333
 
            Semester.year == self.year,
334
 
            Semester.semester == self.semester).one()
335
 
        
336
 
        if self.context is None:
337
 
            raise NotFound()
338
 
 
339
 
    @named_operation('add_worksheet')
 
152
 
 
153
    @named_operation('edit')
340
154
    def add_worksheet(self, req, identifier, name, assessable, data, format):
341
155
        """Takes worksheet data and adds it."""
342
156
        
353
167
        
354
168
        # This call is added for clarity, as the worksheet is implicitly added.        
355
169
        req.store.add(new_worksheet)
356
 
        
357
 
        generate_exerciselist(new_worksheet, req, data)
358
 
        
 
170
 
 
171
        ivle.worksheet.utils.update_exerciselist(new_worksheet)
 
172
 
359
173
        return {"result": "ok"}
360
174
 
361
 
    @named_operation('set_sequence')
362
 
    def seq_sequence(self, req, worksheet_list):
363
 
        """Takes a list of worksheet-seq_no pairs and updates their 
364
 
        corresponding Worksheet objects to match."""
365
 
        
366
 
        for worksheetid, seq_no in worksheet_list:
367
 
            worksheet = req.store.find(Worksheet,
368
 
                Worksheet.offering_id == self.context.id,
369
 
                Worksheet.identifier == worksheetid).one()
370
 
            if worksheet is None:
371
 
                raise NotFound(worksheet)
372
 
            worksheet.seq_no = seq_no
 
175
    @named_operation('edit')
 
176
    def move_up(self, req, worksheetid):
 
177
        """Takes a list of worksheet-seq_no pairs and updates their 
 
178
        corresponding Worksheet objects to match."""
 
179
        
 
180
        worksheet_below = req.store.find(Worksheet,
 
181
            Worksheet.offering_id == self.context.id,
 
182
            Worksheet.identifier == unicode(worksheetid)).one()
 
183
        if worksheet_below is None:
 
184
            raise NotFound('worksheet_below')
 
185
        worksheet_above = req.store.find(Worksheet,
 
186
            Worksheet.offering_id == self.context.id,
 
187
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
 
188
        if worksheet_above is None:
 
189
            raise NotFound('worksheet_above')
 
190
 
 
191
        worksheet_below.seq_no = worksheet_below.seq_no - 1
 
192
        worksheet_above.seq_no = worksheet_above.seq_no + 1
 
193
        
 
194
        return {'result': 'ok'}
 
195
 
 
196
    @named_operation('edit')
 
197
    def move_down(self, req, worksheetid):
 
198
        """Takes a list of worksheet-seq_no pairs and updates their 
 
199
        corresponding Worksheet objects to match."""
 
200
        
 
201
        worksheet_above = req.store.find(Worksheet,
 
202
            Worksheet.offering_id == self.context.id,
 
203
            Worksheet.identifier == unicode(worksheetid)).one()
 
204
        if worksheet_above is None:
 
205
            raise NotFound('worksheet_below')
 
206
        worksheet_below = req.store.find(Worksheet,
 
207
            Worksheet.offering_id == self.context.id,
 
208
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
 
209
        if worksheet_below is None:
 
210
            raise NotFound('worksheet_above')
 
211
 
 
212
        worksheet_below.seq_no = worksheet_below.seq_no - 1
 
213
        worksheet_above.seq_no = worksheet_above.seq_no + 1
373
214
        
374
215
        return {'result': 'ok'}