~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: 2010-02-15 01:12:46 UTC
  • Revision ID: grantw@unimelb.edu.au-20100215011246-2kxz9ro0gnrvqueo
Document how to tag releases in bzr.

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
 
29
                          Offering, Subject, Semester, User, WorksheetExercise
31
30
import ivle.worksheet.utils
32
 
import ivle.conf
33
 
import ivle.webapp.tutorial.test
34
31
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
35
32
                                   require_permission)
36
33
from ivle.webapp.errors import NotFound
37
34
 
38
 
# If True, getattempts or getattempt will allow browsing of inactive/disabled
39
 
# attempts. If False, will not allow this.
40
 
HISTORY_ALLOW_INACTIVE = False
41
35
 
42
36
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
43
37
 
 
38
 
44
39
class AttemptsRESTView(JSONRESTView):
45
40
    '''REST view of a user's attempts at an exercise.'''
46
 
    
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 == unicode(exercise),
55
 
            WorksheetExercise.worksheet_id == Worksheet.id,
56
 
            Worksheet.offering_id == Offering.id,
57
 
            Worksheet.identifier == unicode(worksheet),
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
41
 
66
42
    @require_permission('edit')
67
43
    def GET(self, req):
68
44
        """Handles a GET Attempts action."""
69
45
        attempts = req.store.find(ExerciseAttempt, 
70
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
71
 
                ExerciseAttempt.user_id == self.user.id)
 
46
                ExerciseAttempt.ws_ex_id == self.context.worksheet_exercise.id,
 
47
                ExerciseAttempt.user_id == self.context.user.id)
72
48
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
73
49
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
74
50
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
80
56
    @require_permission('edit')
81
57
    def PUT(self, req, data):
82
58
        """ 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
 
        # Start a console to run the tests on
89
 
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
90
 
        working_dir = os.path.join("/home", req.user.login)
91
 
        cons = ivle.console.Console(req.user.unixid, jail_path, working_dir)
92
 
 
93
 
        # Parse the file into a exercise object using the test suite
94
 
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
95
 
                                                            exercise, cons)
96
 
 
97
 
        # Run the test cases. Get the result back as a JSONable object.
98
 
        # Return it.
99
 
        test_results = exercise_obj.run_tests(data['code'])
100
 
 
101
 
        # Close the console
102
 
        cons.close()
 
59
        test_results = ivle.worksheet.utils.test_exercise_submission(
 
60
            req.config, req.user, self.context.worksheet_exercise.exercise,
 
61
            data['code'])
103
62
 
104
63
        attempt = ivle.database.ExerciseAttempt(user=req.user,
105
 
            worksheet_exercise = self.worksheet_exercise,
 
64
            worksheet_exercise = self.context.worksheet_exercise,
106
65
            date = datetime.datetime.now(),
107
66
            complete = test_results['passed'],
108
67
            text = unicode(data['code'])
114
73
        # has EVER been completed (may be different from "passed", if it has
115
74
        # been completed before), and the total number of attempts.
116
75
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
117
 
                req.store, req.user, self.worksheet_exercise)
 
76
                req.store, req.user, self.context.worksheet_exercise)
118
77
        test_results["completed"] = completed
119
78
        test_results["attempts"] = attempts
120
79
 
124
83
class AttemptRESTView(JSONRESTView):
125
84
    '''REST view of an exercise attempt.'''
126
85
 
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
 
        # XXX Hack around Google Code issue #87
140
 
        # Query from the given date +1 secnod.
141
 
        # Date is in seconds (eg. 3:47:12), while the data is in finer time
142
 
        # (eg. 3:47:12.3625). The query "date <= 3:47:12" will fail because
143
 
        # 3:47:12.3625 is greater. Hence we do the query from +1 second,
144
 
        # "date <= 3:47:13", and it finds the correct submission, UNLESS there
145
 
        # are multiple submissions inside the same second.
146
 
        date += datetime.timedelta(seconds=1)
147
 
 
148
 
        worksheet_exercise = req.store.find(WorksheetExercise,
149
 
            WorksheetExercise.exercise_id == exercise,
150
 
            WorksheetExercise.worksheet_id == Worksheet.id,
151
 
            Worksheet.identifier == worksheet,
152
 
            Worksheet.offering_id == Offering.id,
153
 
            Offering.subject_id == Subject.id,
154
 
            Subject.code == subject,
155
 
            Offering.semester_id == Semester.id,
156
 
            Semester.year == year,
157
 
            Semester.semester == semester).one()
158
 
            
159
 
        attempt = ivle.worksheet.utils.get_exercise_attempt(req.store, user,
160
 
                        worksheet_exercise, as_of=date,
161
 
                        allow_inactive=HISTORY_ALLOW_INACTIVE) 
162
 
 
163
 
        if attempt is None:
164
 
            raise NotFound()
165
 
 
166
 
        self.context = attempt
167
 
 
168
86
    @require_permission('view')
169
87
    def GET(self, req):
170
88
        return {'code': self.context.text}
171
89
 
172
90
 
173
91
class WorksheetExerciseRESTView(JSONRESTView):
174
 
    '''REST view of an exercise.'''
175
 
 
176
 
    def get_permissions(self, user):
177
 
        # XXX: Do it properly.
178
 
        # XXX: Does any user have the ability to save as themselves?
179
 
        # XXX: Does a user EVER have permission to save as another user?
180
 
        if user is not None:
181
 
            return set(['save'])
182
 
        else:
183
 
            return set()
184
 
 
185
 
    @named_operation('save')
 
92
    '''REST view of a worksheet exercise.'''
 
93
 
 
94
    @named_operation('view')
186
95
    def save(self, req, text):
187
96
        # Find the appropriate WorksheetExercise to save to. If its not found,
188
97
        # the user is submitting against a non-existant worksheet/exercise
189
 
        worksheet_exercise = req.store.find(WorksheetExercise,
190
 
            WorksheetExercise.exercise_id == self.exercise,
191
 
            WorksheetExercise.worksheet_id == Worksheet.id,
192
 
            Worksheet.offering_id == Offering.id,
193
 
            Offering.subject_id == Subject.id,
194
 
            Subject.code == self.subject,
195
 
            Offering.semester_id == Semester.id,
196
 
            Semester.year == self.year,
197
 
            Semester.semester == self.semester).one()
198
 
        
199
 
        if worksheet_exercise is None:
200
 
            raise NotFound()
201
98
 
202
99
        old_save = req.store.find(ExerciseSave,
203
 
            ExerciseSave.ws_ex_id == worksheet_exercise.id,
 
100
            ExerciseSave.ws_ex_id == self.context.id,
204
101
            ExerciseSave.user == req.user).one()
205
102
        
206
103
        #Overwrite the old, or create a new if there isn't one
210
107
        else:
211
108
            new_save = old_save
212
109
        
213
 
        new_save.worksheet_exercise = worksheet_exercise
 
110
        new_save.worksheet_exercise = self.context
214
111
        new_save.user = req.user
215
112
        new_save.text = unicode(text)
216
113
        new_save.date = datetime.datetime.now()
218
115
        return {"result": "ok"}
219
116
 
220
117
 
221
 
 
222
 
# Note that this is the view of an existing worksheet. Creation is handled
223
 
# by OfferingRESTView (as offerings have worksheets)
224
 
class WorksheetRESTView(JSONRESTView):
225
 
    """View used to update a worksheet."""
226
 
 
227
 
    def get_permissions(self, user):
228
 
        # XXX: Do it properly.
229
 
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
230
 
        #      under their control
231
 
        if user is not None:
232
 
            if user.admin:
233
 
                return set(['save'])
234
 
            else:
235
 
                return set()
236
 
        else:
237
 
            return set()    
238
 
 
239
 
    def __init__(self, req, **kwargs):
240
 
    
241
 
        self.worksheet = kwargs['worksheet']
242
 
        self.subject = kwargs['subject']
243
 
        self.year = kwargs['year']
244
 
        self.semester = kwargs['semester']
245
 
    
246
 
        self.context = req.store.find(Worksheet,
247
 
            Worksheet.identifier == self.worksheet,
248
 
            Worksheet.offering_id == Offering.id,
249
 
            Offering.subject_id == Subject.id,
250
 
            Subject.code == self.subject,
251
 
            Offering.semester_id == Semester.id,
252
 
            Semester.year == self.year,
253
 
            Semester.semester == self.semester).one()
254
 
        
255
 
        if self.context is None:
256
 
            raise NotFound()
257
 
    
258
 
    @named_operation('save')
259
 
    def save(self, req, name, assessable, data, format):
260
 
        """Takes worksheet data and saves it."""
261
 
        self.context.name = unicode(name)
262
 
        self.context.assessable = self.convert_bool(assessable)
263
 
        self.context.data = unicode(data)
264
 
        self.context.format = unicode(format)
265
 
        ivle.worksheet.utils.update_exerciselist(self.context)
266
 
        
267
 
        return {"result": "ok"}
268
 
 
269
118
class WorksheetsRESTView(JSONRESTView):
270
119
    """View used to update and create Worksheets."""
271
 
    
272
 
    def get_permissions(self, user):
273
 
        # XXX: Do it properly.
274
 
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
275
 
        #      under their control
276
 
        if user is not None:
277
 
            if user.admin:
278
 
                return set(['edit'])
279
 
            else:
280
 
                return set()
281
 
        else:
282
 
            return set()
283
 
 
284
 
    def __init__(self, req, **kwargs):
285
 
    
286
 
        self.subject = kwargs['subject']
287
 
        self.year = kwargs['year']
288
 
        self.semester = kwargs['semester']
289
 
    
290
 
        self.context = req.store.find(Offering,
291
 
            Offering.subject_id == Subject.id,
292
 
            Subject.code == self.subject,
293
 
            Offering.semester_id == Semester.id,
294
 
            Semester.year == self.year,
295
 
            Semester.semester == self.semester).one()
296
 
        
297
 
        if self.context is None:
298
 
            raise NotFound()
299
 
 
300
 
    @named_operation('edit')
301
 
    def add_worksheet(self, req, identifier, name, assessable, data, format):
302
 
        """Takes worksheet data and adds it."""
303
 
        
304
 
        new_worksheet = Worksheet()
305
 
        new_worksheet.seq_no = self.context.worksheets.count()
306
 
        # Setting new_worksheet.offering implicitly adds new_worksheet,
307
 
        # hence worksheets.count MUST be called above it
308
 
        new_worksheet.offering = self.context
309
 
        new_worksheet.identifier = unicode(identifier)
310
 
        new_worksheet.name = unicode(name)
311
 
        new_worksheet.assessable = self.convert_bool(assessable)
312
 
        new_worksheet.data = unicode(data)
313
 
        new_worksheet.format = unicode(format)
314
 
        
315
 
        # This call is added for clarity, as the worksheet is implicitly added.        
316
 
        req.store.add(new_worksheet)
317
 
 
318
 
        ivle.worksheet.utils.update_exerciselist(new_worksheet)
319
 
 
320
 
        return {"result": "ok"}
321
 
 
322
 
    @named_operation('edit')
 
120
 
 
121
    @named_operation('edit_worksheets')
323
122
    def move_up(self, req, worksheetid):
324
123
        """Takes a list of worksheet-seq_no pairs and updates their 
325
124
        corresponding Worksheet objects to match."""
340
139
        
341
140
        return {'result': 'ok'}
342
141
 
343
 
    @named_operation('edit')
 
142
    @named_operation('edit_worksheets')
344
143
    def move_down(self, req, worksheetid):
345
144
        """Takes a list of worksheet-seq_no pairs and updates their 
346
145
        corresponding Worksheet objects to match."""