~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-25 07:34:50 UTC
  • Revision ID: grantw@unimelb.edu.au-20100225073450-zcl8ev5hlyhbszeu
Activate the Storm C extensions if possible. Moar speed.

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.console
27
27
import ivle.database
28
28
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
29
 
                          Offering, Subject, Semester, WorksheetExercise
 
29
                          Offering, Subject, Semester, User, WorksheetExercise
30
30
import ivle.worksheet.utils
31
 
import ivle.webapp.tutorial.test
32
31
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
33
32
                                   require_permission)
34
33
from ivle.webapp.errors import NotFound
35
34
 
36
 
# If True, getattempts or getattempt will allow browsing of inactive/disabled
37
 
# attempts. If False, will not allow this.
38
 
HISTORY_ALLOW_INACTIVE = False
39
35
 
40
36
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
41
37
 
 
38
 
42
39
class AttemptsRESTView(JSONRESTView):
43
40
    '''REST view of a user's attempts at an exercise.'''
44
 
    
45
 
    def __init__(self, req, subject, year, semester, worksheet, 
46
 
                                                exercise, username):
47
 
        self.user = ivle.database.User.get_by_login(req.store, username)
48
 
        if self.user is None:
49
 
            raise NotFound()
50
 
        
51
 
        self.worksheet_exercise = req.store.find(WorksheetExercise,
52
 
            WorksheetExercise.exercise_id == unicode(exercise),
53
 
            WorksheetExercise.worksheet_id == Worksheet.id,
54
 
            Worksheet.offering_id == Offering.id,
55
 
            Worksheet.identifier == unicode(worksheet),
56
 
            Offering.subject_id == Subject.id,
57
 
            Subject.short_name == subject,
58
 
            Offering.semester_id == Semester.id,
59
 
            Semester.year == year,
60
 
            Semester.semester == semester).one()
61
 
        
62
 
        self.context = self.user # XXX: Not quite right.
63
41
 
64
42
    @require_permission('edit')
65
43
    def GET(self, req):
66
44
        """Handles a GET Attempts action."""
67
45
        attempts = req.store.find(ExerciseAttempt, 
68
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
69
 
                ExerciseAttempt.user_id == self.user.id)
 
46
                ExerciseAttempt.ws_ex_id == self.context.worksheet_exercise.id,
 
47
                ExerciseAttempt.user_id == self.context.user.id)
70
48
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
71
49
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
72
50
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
78
56
    @require_permission('edit')
79
57
    def PUT(self, req, data):
80
58
        """ Tests the given submission """
81
 
        exercise = req.store.find(Exercise, 
82
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
83
 
        if exercise is None:
84
 
            raise NotFound()
85
 
 
86
 
        # Start a console to run the tests on
87
 
        jail_path = os.path.join(req.config['paths']['jails']['mounts'],
88
 
                                 req.user.login)
89
 
        working_dir = os.path.join("/home", req.user.login)
90
 
        cons = ivle.console.Console(req.config, req.user.unixid, jail_path,
91
 
                                    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.short_name == 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}
173
91
class WorksheetExerciseRESTView(JSONRESTView):
174
92
    '''REST view of a worksheet exercise.'''
175
93
 
176
 
    def __init__(self, req, subject, year, semester, worksheet, exercise):
177
 
        self.context = req.store.find(WorksheetExercise,
178
 
            WorksheetExercise.exercise_id == exercise,
179
 
            WorksheetExercise.worksheet_id == Worksheet.id,
180
 
            Worksheet.offering_id == Offering.id,
181
 
            Offering.subject_id == Subject.id,
182
 
            Subject.short_name == subject,
183
 
            Offering.semester_id == Semester.id,
184
 
            Semester.year == year,
185
 
            Semester.semester == semester).one()
186
 
        
187
 
        if self.context is None:
188
 
            raise NotFound()
189
 
 
190
94
    @named_operation('view')
191
95
    def save(self, req, text):
192
96
        # Find the appropriate WorksheetExercise to save to. If its not found,
211
115
        return {"result": "ok"}
212
116
 
213
117
 
214
 
# Note that this is the view of an existing worksheet. Creation is handled
215
 
# by OfferingRESTView (as offerings have worksheets)
216
 
class WorksheetRESTView(JSONRESTView):
217
 
    """View used to update a worksheet."""
218
 
 
219
 
    def __init__(self, req, **kwargs):
220
 
    
221
 
        self.worksheet = kwargs['worksheet']
222
 
        self.subject = kwargs['subject']
223
 
        self.year = kwargs['year']
224
 
        self.semester = kwargs['semester']
225
 
    
226
 
        self.context = req.store.find(Worksheet,
227
 
            Worksheet.identifier == self.worksheet,
228
 
            Worksheet.offering_id == Offering.id,
229
 
            Offering.subject_id == Subject.id,
230
 
            Subject.short_name == self.subject,
231
 
            Offering.semester_id == Semester.id,
232
 
            Semester.year == self.year,
233
 
            Semester.semester == self.semester).one()
234
 
        
235
 
        if self.context is None:
236
 
            raise NotFound()
237
 
    
238
 
    @named_operation('edit')
239
 
    def save(self, req, name, assessable, data, format):
240
 
        """Takes worksheet data and saves it."""
241
 
        self.context.name = unicode(name)
242
 
        self.context.assessable = self.convert_bool(assessable)
243
 
        self.context.data = unicode(data)
244
 
        self.context.format = unicode(format)
245
 
        ivle.worksheet.utils.update_exerciselist(self.context)
246
 
        
247
 
        return {"result": "ok"}
248
 
 
249
118
class WorksheetsRESTView(JSONRESTView):
250
119
    """View used to update and create Worksheets."""
251
 
    
252
 
    def __init__(self, req, **kwargs):
253
 
    
254
 
        self.subject = kwargs['subject']
255
 
        self.year = kwargs['year']
256
 
        self.semester = kwargs['semester']
257
 
    
258
 
        self.context = req.store.find(Offering,
259
 
            Offering.subject_id == Subject.id,
260
 
            Subject.short_name == self.subject,
261
 
            Offering.semester_id == Semester.id,
262
 
            Semester.year == self.year,
263
 
            Semester.semester == self.semester).one()
264
 
        
265
 
        if self.context is None:
266
 
            raise NotFound()
267
 
 
268
 
    @named_operation('edit')
269
 
    def add_worksheet(self, req, identifier, name, assessable, data, format):
270
 
        """Takes worksheet data and adds it."""
271
 
        
272
 
        new_worksheet = Worksheet()
273
 
        new_worksheet.seq_no = self.context.worksheets.count()
274
 
        # Setting new_worksheet.offering implicitly adds new_worksheet,
275
 
        # hence worksheets.count MUST be called above it
276
 
        new_worksheet.offering = self.context
277
 
        new_worksheet.identifier = unicode(identifier)
278
 
        new_worksheet.name = unicode(name)
279
 
        new_worksheet.assessable = self.convert_bool(assessable)
280
 
        new_worksheet.data = unicode(data)
281
 
        new_worksheet.format = unicode(format)
282
 
        
283
 
        # This call is added for clarity, as the worksheet is implicitly added.        
284
 
        req.store.add(new_worksheet)
285
 
 
286
 
        ivle.worksheet.utils.update_exerciselist(new_worksheet)
287
 
 
288
 
        return {"result": "ok"}
289
 
 
290
 
    @named_operation('edit')
 
120
 
 
121
    @named_operation('edit_worksheets')
291
122
    def move_up(self, req, worksheetid):
292
123
        """Takes a list of worksheet-seq_no pairs and updates their 
293
124
        corresponding Worksheet objects to match."""
308
139
        
309
140
        return {'result': 'ok'}
310
141
 
311
 
    @named_operation('edit')
 
142
    @named_operation('edit_worksheets')
312
143
    def move_down(self, req, worksheetid):
313
144
        """Takes a list of worksheet-seq_no pairs and updates their 
314
145
        corresponding Worksheet objects to match."""