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

« back to all changes in this revision

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

MergedĀ fromĀ trunk

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
22
23
import datetime
23
 
 
24
24
import genshi
25
 
from storm.locals import Store
26
25
 
 
26
import ivle.util
 
27
import ivle.console
27
28
import ivle.database
28
29
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
29
 
                          Offering, Subject, Semester, User, WorksheetExercise
 
30
                          Offering, Subject, Semester, WorksheetExercise
30
31
import ivle.worksheet.utils
 
32
import ivle.conf
 
33
import ivle.webapp.tutorial.test
31
34
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
32
35
                                   require_permission)
33
36
from ivle.webapp.errors import NotFound
34
37
 
 
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
35
41
 
36
42
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
37
43
 
38
 
 
39
44
class AttemptsRESTView(JSONRESTView):
40
45
    '''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 == 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.
41
64
 
42
65
    @require_permission('edit')
43
66
    def GET(self, req):
44
67
        """Handles a GET Attempts action."""
45
68
        attempts = req.store.find(ExerciseAttempt, 
46
 
                ExerciseAttempt.ws_ex_id == self.context.worksheet_exercise.id,
47
 
                ExerciseAttempt.user_id == self.context.user.id)
 
69
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
 
70
                ExerciseAttempt.user_id == self.user.id)
48
71
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
49
72
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
50
73
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
56
79
    @require_permission('edit')
57
80
    def PUT(self, req, data):
58
81
        """ Tests the given submission """
59
 
        test_results = ivle.worksheet.utils.test_exercise_submission(
60
 
            req.config, req.user, self.context.worksheet_exercise.exercise,
61
 
            data['code'])
 
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()
62
102
 
63
103
        attempt = ivle.database.ExerciseAttempt(user=req.user,
64
 
            worksheet_exercise = self.context.worksheet_exercise,
 
104
            worksheet_exercise = self.worksheet_exercise,
65
105
            date = datetime.datetime.now(),
66
106
            complete = test_results['passed'],
67
107
            text = unicode(data['code'])
73
113
        # has EVER been completed (may be different from "passed", if it has
74
114
        # been completed before), and the total number of attempts.
75
115
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
76
 
                req.store, req.user, self.context.worksheet_exercise)
 
116
                req.store, req.user, self.worksheet_exercise)
77
117
        test_results["completed"] = completed
78
118
        test_results["attempts"] = attempts
79
119
 
83
123
class AttemptRESTView(JSONRESTView):
84
124
    '''REST view of an exercise attempt.'''
85
125
 
 
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 = ivle.worksheet.utils.get_exercise_attempt(req.store, user,
 
150
                        worksheet_exercise, as_of=date,
 
151
                        allow_inactive=HISTORY_ALLOW_INACTIVE) 
 
152
 
 
153
        if attempt is None:
 
154
            raise NotFound()
 
155
 
 
156
        self.context = attempt
 
157
 
86
158
    @require_permission('view')
87
159
    def GET(self, req):
88
160
        return {'code': self.context.text}
89
161
 
90
162
 
91
163
class WorksheetExerciseRESTView(JSONRESTView):
92
 
    '''REST view of a worksheet exercise.'''
93
 
 
94
 
    @named_operation('view')
 
164
    '''REST view of an exercise.'''
 
165
 
 
166
    def get_permissions(self, user):
 
167
        # XXX: Do it properly.
 
168
        # XXX: Does any user have the ability to save as themselves?
 
169
        # XXX: Does a user EVER have permission to save as another user?
 
170
        if user is not None:
 
171
            return set(['save'])
 
172
        else:
 
173
            return set()
 
174
 
 
175
    @named_operation('save')
95
176
    def save(self, req, text):
96
177
        # Find the appropriate WorksheetExercise to save to. If its not found,
97
178
        # the user is submitting against a non-existant worksheet/exercise
 
179
        worksheet_exercise = req.store.find(WorksheetExercise,
 
180
            WorksheetExercise.exercise_id == self.exercise,
 
181
            WorksheetExercise.worksheet_id == Worksheet.id,
 
182
            Worksheet.offering_id == Offering.id,
 
183
            Offering.subject_id == Subject.id,
 
184
            Subject.code == self.subject,
 
185
            Offering.semester_id == Semester.id,
 
186
            Semester.year == self.year,
 
187
            Semester.semester == self.semester).one()
 
188
        
 
189
        if worksheet_exercise is None:
 
190
            raise NotFound()
98
191
 
99
192
        old_save = req.store.find(ExerciseSave,
100
 
            ExerciseSave.ws_ex_id == self.context.id,
 
193
            ExerciseSave.ws_ex_id == worksheet_exercise.id,
101
194
            ExerciseSave.user == req.user).one()
102
195
        
103
196
        #Overwrite the old, or create a new if there isn't one
107
200
        else:
108
201
            new_save = old_save
109
202
        
110
 
        new_save.worksheet_exercise = self.context
 
203
        new_save.worksheet_exercise = worksheet_exercise
111
204
        new_save.user = req.user
112
205
        new_save.text = unicode(text)
113
206
        new_save.date = datetime.datetime.now()
115
208
        return {"result": "ok"}
116
209
 
117
210
 
 
211
 
 
212
# Note that this is the view of an existing worksheet. Creation is handled
 
213
# by OfferingRESTView (as offerings have worksheets)
 
214
class WorksheetRESTView(JSONRESTView):
 
215
    """View used to update a worksheet."""
 
216
 
 
217
    def get_permissions(self, user):
 
218
        # XXX: Do it properly.
 
219
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
 
220
        #      under their control
 
221
        if user is not None:
 
222
            if user.admin:
 
223
                return set(['save'])
 
224
            else:
 
225
                return set()
 
226
        else:
 
227
            return set()    
 
228
 
 
229
    def __init__(self, req, **kwargs):
 
230
    
 
231
        self.worksheet = kwargs['worksheet']
 
232
        self.subject = kwargs['subject']
 
233
        self.year = kwargs['year']
 
234
        self.semester = kwargs['semester']
 
235
    
 
236
        self.context = req.store.find(Worksheet,
 
237
            Worksheet.identifier == self.worksheet,
 
238
            Worksheet.offering_id == Offering.id,
 
239
            Offering.subject_id == Subject.id,
 
240
            Subject.code == self.subject,
 
241
            Offering.semester_id == Semester.id,
 
242
            Semester.year == self.year,
 
243
            Semester.semester == self.semester).one()
 
244
        
 
245
        if self.context is None:
 
246
            raise NotFound()
 
247
    
 
248
    @named_operation('save')
 
249
    def save(self, req, name, assessable, data, format):
 
250
        """Takes worksheet data and saves it."""
 
251
        self.context.name = unicode(name)
 
252
        self.context.assessable = self.convert_bool(assessable)
 
253
        self.context.data = unicode(data)
 
254
        self.context.format = unicode(format)
 
255
        ivle.worksheet.utils.update_exerciselist(self.context)
 
256
        
 
257
        return {"result": "ok"}
 
258
 
118
259
class WorksheetsRESTView(JSONRESTView):
119
260
    """View used to update and create Worksheets."""
120
 
 
121
 
    @named_operation('edit_worksheets')
 
261
    
 
262
    def get_permissions(self, user):
 
263
        # XXX: Do it properly.
 
264
        # XXX: Lecturers should be allowed to add worksheets Only to subjects
 
265
        #      under their control
 
266
        if user is not None:
 
267
            if user.admin:
 
268
                return set(['edit'])
 
269
            else:
 
270
                return set()
 
271
        else:
 
272
            return set()
 
273
 
 
274
    def __init__(self, req, **kwargs):
 
275
    
 
276
        self.subject = kwargs['subject']
 
277
        self.year = kwargs['year']
 
278
        self.semester = kwargs['semester']
 
279
    
 
280
        self.context = req.store.find(Offering,
 
281
            Offering.subject_id == Subject.id,
 
282
            Subject.code == self.subject,
 
283
            Offering.semester_id == Semester.id,
 
284
            Semester.year == self.year,
 
285
            Semester.semester == self.semester).one()
 
286
        
 
287
        if self.context is None:
 
288
            raise NotFound()
 
289
 
 
290
    @named_operation('edit')
 
291
    def add_worksheet(self, req, identifier, name, assessable, data, format):
 
292
        """Takes worksheet data and adds it."""
 
293
        
 
294
        new_worksheet = Worksheet()
 
295
        new_worksheet.seq_no = self.context.worksheets.count()
 
296
        # Setting new_worksheet.offering implicitly adds new_worksheet,
 
297
        # hence worksheets.count MUST be called above it
 
298
        new_worksheet.offering = self.context
 
299
        new_worksheet.identifier = unicode(identifier)
 
300
        new_worksheet.name = unicode(name)
 
301
        new_worksheet.assessable = self.convert_bool(assessable)
 
302
        new_worksheet.data = unicode(data)
 
303
        new_worksheet.format = unicode(format)
 
304
        
 
305
        # This call is added for clarity, as the worksheet is implicitly added.        
 
306
        req.store.add(new_worksheet)
 
307
 
 
308
        ivle.worksheet.utils.update_exerciselist(new_worksheet)
 
309
 
 
310
        return {"result": "ok"}
 
311
 
 
312
    @named_operation('edit')
122
313
    def move_up(self, req, worksheetid):
123
314
        """Takes a list of worksheet-seq_no pairs and updates their 
124
315
        corresponding Worksheet objects to match."""
139
330
        
140
331
        return {'result': 'ok'}
141
332
 
142
 
    @named_operation('edit_worksheets')
 
333
    @named_operation('edit')
143
334
    def move_down(self, req, worksheetid):
144
335
        """Takes a list of worksheet-seq_no pairs and updates their 
145
336
        corresponding Worksheet objects to match."""