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

« back to all changes in this revision

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

Install to /usr/local/lib/python2.5/site-packages, not
/usr/lib/python2.5/site-packages.

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
 
 
25
import ivle.util
26
26
import ivle.console
27
27
import ivle.database
28
 
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
29
 
                          Offering, Subject, Semester, WorksheetExercise
30
 
import ivle.worksheet.utils
 
28
import ivle.worksheet
 
29
import ivle.conf
31
30
import ivle.webapp.tutorial.test
 
31
 
32
32
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
33
33
                                   require_permission)
34
34
from ivle.webapp.errors import NotFound
39
39
 
40
40
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
41
41
 
 
42
 
42
43
class AttemptsRESTView(JSONRESTView):
43
44
    '''REST view of a user's attempts at an exercise.'''
44
 
    
45
 
    def __init__(self, req, subject, year, semester, worksheet, 
46
 
                                                exercise, username):
 
45
    def __init__(self, req, subject, worksheet, exercise, username):
 
46
        # TODO: Find exercise within worksheet.
47
47
        self.user = ivle.database.User.get_by_login(req.store, username)
48
48
        if self.user is None:
49
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
 
        
 
50
        self.exercise = exercise
62
51
        self.context = self.user # XXX: Not quite right.
63
52
 
64
53
    @require_permission('edit')
65
54
    def GET(self, req):
66
55
        """Handles a GET Attempts action."""
67
 
        attempts = req.store.find(ExerciseAttempt, 
68
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
69
 
                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)
70
61
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
71
62
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
72
63
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
78
69
    @require_permission('edit')
79
70
    def PUT(self, req, data):
80
71
        """ Tests the given submission """
81
 
        exercise = req.store.find(Exercise, 
82
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
83
 
        if exercise is None:
 
72
        exercisefile = ivle.util.open_exercise_file(self.exercise)
 
73
        if exercisefile is None:
84
74
            raise NotFound()
85
75
 
86
76
        # Start a console to run the tests on
87
 
        jail_path = os.path.join(req.config['paths']['jails']['mounts'],
88
 
                                 req.user.login)
 
77
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
89
78
        working_dir = os.path.join("/home", req.user.login)
90
 
        cons = ivle.console.Console(req.config, req.user.unixid, jail_path,
91
 
                                    working_dir)
 
79
        cons = ivle.console.Console(req.user.unixid, jail_path, working_dir)
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
 
113
105
        # Query the DB to get an updated score on whether or not this problem
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
 
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
117
 
                req.store, req.user, self.worksheet_exercise)
 
108
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
 
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
 
        # 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) 
 
130
        exercise = ivle.database.Exercise.get_by_name(req.store, exercise)
 
131
        attempt = ivle.worksheet.get_exercise_attempt(req.store, user,
 
132
            exercise, as_of=date, allow_inactive=HISTORY_ALLOW_INACTIVE)
162
133
 
163
134
        if attempt is None:
164
135
            raise NotFound()
170
141
        return {'code': self.context.text}
171
142
 
172
143
 
173
 
class WorksheetExerciseRESTView(JSONRESTView):
174
 
    '''REST view of a worksheet exercise.'''
175
 
 
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
 
    @named_operation('view')
 
144
class ExerciseRESTView(JSONRESTView):
 
145
    '''REST view of an exercise.'''
 
146
 
 
147
    def get_permissions(self, user):
 
148
        # XXX: Do it properly.
 
149
        if user is not None:
 
150
            return set(['save'])
 
151
        else:
 
152
            return set()
 
153
 
 
154
    @named_operation('save')
191
155
    def save(self, req, text):
192
 
        # Find the appropriate WorksheetExercise to save to. If its not found,
193
 
        # the user is submitting against a non-existant worksheet/exercise
194
 
 
195
 
        old_save = req.store.find(ExerciseSave,
196
 
            ExerciseSave.ws_ex_id == self.context.id,
197
 
            ExerciseSave.user == req.user).one()
198
 
        
199
 
        #Overwrite the old, or create a new if there isn't one
200
 
        if old_save is None:
201
 
            new_save = ExerciseSave()
202
 
            req.store.add(new_save)
203
 
        else:
204
 
            new_save = old_save
205
 
        
206
 
        new_save.worksheet_exercise = self.context
207
 
        new_save.user = req.user
208
 
        new_save.text = unicode(text)
209
 
        new_save.date = datetime.datetime.now()
210
 
 
211
 
        return {"result": "ok"}
212
 
 
213
 
 
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
 
class WorksheetsRESTView(JSONRESTView):
250
 
    """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')
291
 
    def move_up(self, req, worksheetid):
292
 
        """Takes a list of worksheet-seq_no pairs and updates their 
293
 
        corresponding Worksheet objects to match."""
294
 
        
295
 
        worksheet_below = req.store.find(Worksheet,
296
 
            Worksheet.offering_id == self.context.id,
297
 
            Worksheet.identifier == unicode(worksheetid)).one()
298
 
        if worksheet_below is None:
299
 
            raise NotFound('worksheet_below')
300
 
        worksheet_above = req.store.find(Worksheet,
301
 
            Worksheet.offering_id == self.context.id,
302
 
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
303
 
        if worksheet_above is None:
304
 
            raise NotFound('worksheet_above')
305
 
 
306
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
307
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
308
 
        
309
 
        return {'result': 'ok'}
310
 
 
311
 
    @named_operation('edit')
312
 
    def move_down(self, req, worksheetid):
313
 
        """Takes a list of worksheet-seq_no pairs and updates their 
314
 
        corresponding Worksheet objects to match."""
315
 
        
316
 
        worksheet_above = req.store.find(Worksheet,
317
 
            Worksheet.offering_id == self.context.id,
318
 
            Worksheet.identifier == unicode(worksheetid)).one()
319
 
        if worksheet_above is None:
320
 
            raise NotFound('worksheet_below')
321
 
        worksheet_below = req.store.find(Worksheet,
322
 
            Worksheet.offering_id == self.context.id,
323
 
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
324
 
        if worksheet_below is None:
325
 
            raise NotFound('worksheet_above')
326
 
 
327
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
328
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
329
 
        
330
 
        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"}