~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 05:37:50 UTC
  • Revision ID: grantw@unimelb.edu.au-20100215053750-hihmegnp8e7dshc2
Ignore test coverage files.

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.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()
 
59
        test_results = ivle.worksheet.utils.test_exercise_submission(
 
60
            req.config, req.user, self.context.worksheet_exercise.exercise,
 
61
            data['code'])
102
62
 
103
63
        attempt = ivle.database.ExerciseAttempt(user=req.user,
104
 
            worksheet_exercise = self.worksheet_exercise,
 
64
            worksheet_exercise = self.context.worksheet_exercise,
105
65
            date = datetime.datetime.now(),
106
66
            complete = test_results['passed'],
107
67
            text = unicode(data['code'])
113
73
        # has EVER been completed (may be different from "passed", if it has
114
74
        # been completed before), and the total number of attempts.
115
75
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
116
 
                req.store, req.user, self.worksheet_exercise)
 
76
                req.store, req.user, self.context.worksheet_exercise)
117
77
        test_results["completed"] = completed
118
78
        test_results["attempts"] = attempts
119
79
 
123
83
class AttemptRESTView(JSONRESTView):
124
84
    '''REST view of an exercise attempt.'''
125
85
 
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
 
        # XXX Hack around Google Code issue #87
139
 
        # Query from the given date +1 secnod.
140
 
        # Date is in seconds (eg. 3:47:12), while the data is in finer time
141
 
        # (eg. 3:47:12.3625). The query "date <= 3:47:12" will fail because
142
 
        # 3:47:12.3625 is greater. Hence we do the query from +1 second,
143
 
        # "date <= 3:47:13", and it finds the correct submission, UNLESS there
144
 
        # are multiple submissions inside the same second.
145
 
        date += datetime.timedelta(seconds=1)
146
 
 
147
 
        worksheet_exercise = req.store.find(WorksheetExercise,
148
 
            WorksheetExercise.exercise_id == exercise,
149
 
            WorksheetExercise.worksheet_id == Worksheet.id,
150
 
            Worksheet.identifier == worksheet,
151
 
            Worksheet.offering_id == Offering.id,
152
 
            Offering.subject_id == Subject.id,
153
 
            Subject.short_name == subject,
154
 
            Offering.semester_id == Semester.id,
155
 
            Semester.year == year,
156
 
            Semester.semester == semester).one()
157
 
            
158
 
        attempt = ivle.worksheet.utils.get_exercise_attempt(req.store, user,
159
 
                        worksheet_exercise, as_of=date,
160
 
                        allow_inactive=HISTORY_ALLOW_INACTIVE) 
161
 
 
162
 
        if attempt is None:
163
 
            raise NotFound()
164
 
 
165
 
        self.context = attempt
166
 
 
167
86
    @require_permission('view')
168
87
    def GET(self, req):
169
88
        return {'code': self.context.text}
172
91
class WorksheetExerciseRESTView(JSONRESTView):
173
92
    '''REST view of a worksheet exercise.'''
174
93
 
175
 
    def __init__(self, req, subject, year, semester, worksheet, exercise):
176
 
        self.context = req.store.find(WorksheetExercise,
177
 
            WorksheetExercise.exercise_id == exercise,
178
 
            WorksheetExercise.worksheet_id == Worksheet.id,
179
 
            Worksheet.offering_id == Offering.id,
180
 
            Offering.subject_id == Subject.id,
181
 
            Subject.short_name == subject,
182
 
            Offering.semester_id == Semester.id,
183
 
            Semester.year == year,
184
 
            Semester.semester == semester).one()
185
 
        
186
 
        if self.context is None:
187
 
            raise NotFound()
188
 
 
189
94
    @named_operation('view')
190
95
    def save(self, req, text):
191
96
        # Find the appropriate WorksheetExercise to save to. If its not found,
210
115
        return {"result": "ok"}
211
116
 
212
117
 
213
 
# Note that this is the view of an existing worksheet. Creation is handled
214
 
# by OfferingRESTView (as offerings have worksheets)
215
 
class WorksheetRESTView(JSONRESTView):
216
 
    """View used to update a worksheet."""
217
 
 
218
 
    def __init__(self, req, **kwargs):
219
 
    
220
 
        self.worksheet = kwargs['worksheet']
221
 
        self.subject = kwargs['subject']
222
 
        self.year = kwargs['year']
223
 
        self.semester = kwargs['semester']
224
 
    
225
 
        self.context = req.store.find(Worksheet,
226
 
            Worksheet.identifier == self.worksheet,
227
 
            Worksheet.offering_id == Offering.id,
228
 
            Offering.subject_id == Subject.id,
229
 
            Subject.short_name == self.subject,
230
 
            Offering.semester_id == Semester.id,
231
 
            Semester.year == self.year,
232
 
            Semester.semester == self.semester).one()
233
 
        
234
 
        if self.context is None:
235
 
            raise NotFound()
236
 
    
237
 
    @named_operation('edit')
238
 
    def save(self, req, name, assessable, data, format):
239
 
        """Takes worksheet data and saves it."""
240
 
        self.context.name = unicode(name)
241
 
        self.context.assessable = self.convert_bool(assessable)
242
 
        self.context.data = unicode(data)
243
 
        self.context.format = unicode(format)
244
 
        ivle.worksheet.utils.update_exerciselist(self.context)
245
 
        
246
 
        return {"result": "ok"}
247
 
 
248
118
class WorksheetsRESTView(JSONRESTView):
249
119
    """View used to update and create Worksheets."""
250
 
    
251
 
    def __init__(self, req, **kwargs):
252
 
    
253
 
        self.subject = kwargs['subject']
254
 
        self.year = kwargs['year']
255
 
        self.semester = kwargs['semester']
256
 
    
257
 
        self.context = req.store.find(Offering,
258
 
            Offering.subject_id == Subject.id,
259
 
            Subject.short_name == self.subject,
260
 
            Offering.semester_id == Semester.id,
261
 
            Semester.year == self.year,
262
 
            Semester.semester == self.semester).one()
263
 
        
264
 
        if self.context is None:
265
 
            raise NotFound()
266
 
 
267
 
    @named_operation('edit')
268
 
    def add_worksheet(self, req, identifier, name, assessable, data, format):
269
 
        """Takes worksheet data and adds it."""
270
 
        
271
 
        new_worksheet = Worksheet()
272
 
        new_worksheet.seq_no = self.context.worksheets.count()
273
 
        # Setting new_worksheet.offering implicitly adds new_worksheet,
274
 
        # hence worksheets.count MUST be called above it
275
 
        new_worksheet.offering = self.context
276
 
        new_worksheet.identifier = unicode(identifier)
277
 
        new_worksheet.name = unicode(name)
278
 
        new_worksheet.assessable = self.convert_bool(assessable)
279
 
        new_worksheet.data = unicode(data)
280
 
        new_worksheet.format = unicode(format)
281
 
        
282
 
        # This call is added for clarity, as the worksheet is implicitly added.        
283
 
        req.store.add(new_worksheet)
284
 
 
285
 
        ivle.worksheet.utils.update_exerciselist(new_worksheet)
286
 
 
287
 
        return {"result": "ok"}
288
 
 
289
 
    @named_operation('edit')
 
120
 
 
121
    @named_operation('edit_worksheets')
290
122
    def move_up(self, req, worksheetid):
291
123
        """Takes a list of worksheet-seq_no pairs and updates their 
292
124
        corresponding Worksheet objects to match."""
307
139
        
308
140
        return {'result': 'ok'}
309
141
 
310
 
    @named_operation('edit')
 
142
    @named_operation('edit_worksheets')
311
143
    def move_down(self, req, worksheetid):
312
144
        """Takes a list of worksheet-seq_no pairs and updates their 
313
145
        corresponding Worksheet objects to match."""