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

« back to all changes in this revision

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

Remove phpBB3 configuration from ivle-config.

Now the only forum-related thing in the main IVLE configuration is the forum
secret. That also now persists across configurations, rather than being
clobbered.

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
31
29
import ivle.conf
32
30
import ivle.webapp.tutorial.test
 
31
 
33
32
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
34
33
                                   require_permission)
35
34
from ivle.webapp.errors import NotFound
40
39
 
41
40
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
42
41
 
 
42
 
43
43
class AttemptsRESTView(JSONRESTView):
44
44
    '''REST view of a user's attempts at an exercise.'''
45
 
    
46
 
    def __init__(self, req, subject, year, semester, worksheet, 
47
 
                                                exercise, username):
 
45
    def __init__(self, req, subject, worksheet, exercise, username):
 
46
        # TODO: Find exercise within worksheet.
48
47
        self.user = ivle.database.User.get_by_login(req.store, username)
49
48
        if self.user is None:
50
49
            raise NotFound()
51
 
        
52
 
        self.worksheet_exercise = req.store.find(WorksheetExercise,
53
 
            WorksheetExercise.exercise_id == unicode(exercise),
54
 
            WorksheetExercise.worksheet_id == Worksheet.id,
55
 
            Worksheet.offering_id == Offering.id,
56
 
            Worksheet.identifier == unicode(worksheet),
57
 
            Offering.subject_id == Subject.id,
58
 
            Subject.short_name == subject,
59
 
            Offering.semester_id == Semester.id,
60
 
            Semester.year == year,
61
 
            Semester.semester == semester).one()
62
 
        
 
50
        self.exercise = exercise
63
51
        self.context = self.user # XXX: Not quite right.
64
52
 
65
53
    @require_permission('edit')
66
54
    def GET(self, req):
67
55
        """Handles a GET Attempts action."""
68
 
        attempts = req.store.find(ExerciseAttempt, 
69
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
70
 
                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)
71
61
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
72
62
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
73
63
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
79
69
    @require_permission('edit')
80
70
    def PUT(self, req, data):
81
71
        """ Tests the given submission """
82
 
        exercise = req.store.find(Exercise, 
83
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
84
 
        if exercise is None:
 
72
        exercisefile = ivle.util.open_exercise_file(self.exercise)
 
73
        if exercisefile is None:
85
74
            raise NotFound()
86
75
 
87
76
        # Start a console to run the tests on
91
80
 
92
81
        # Parse the file into a exercise object using the test suite
93
82
        exercise_obj = ivle.webapp.tutorial.test.parse_exercise_file(
94
 
                                                            exercise, cons)
 
83
                                                            exercisefile, cons)
 
84
        exercisefile.close()
95
85
 
96
86
        # Run the test cases. Get the result back as a JSONable object.
97
87
        # Return it.
100
90
        # Close the console
101
91
        cons.close()
102
92
 
 
93
        # Get the Exercise from the database
 
94
        exercise = ivle.database.Exercise.get_by_name(req.store, self.exercise)
 
95
 
103
96
        attempt = ivle.database.ExerciseAttempt(user=req.user,
104
 
            worksheet_exercise = self.worksheet_exercise,
105
 
            date = datetime.datetime.now(),
106
 
            complete = test_results['passed'],
107
 
            text = unicode(data['code'])
108
 
        )
 
97
                                                exercise=exercise,
 
98
                                                date=datetime.datetime.now(),
 
99
                                                complete=test_results['passed'],
 
100
                                                # XXX
 
101
                                                text=unicode(data['code']))
109
102
 
110
103
        req.store.add(attempt)
111
104
 
112
105
        # Query the DB to get an updated score on whether or not this problem
113
106
        # has EVER been completed (may be different from "passed", if it has
114
107
        # been completed before), and the total number of attempts.
115
 
        completed, attempts = ivle.worksheet.utils.get_exercise_status(
116
 
                req.store, req.user, self.worksheet_exercise)
 
108
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
 
109
            req.user, exercise)
117
110
        test_results["completed"] = completed
118
111
        test_results["attempts"] = attempts
119
112
 
123
116
class AttemptRESTView(JSONRESTView):
124
117
    '''REST view of an exercise attempt.'''
125
118
 
126
 
    def __init__(self, req, subject, year, semester, worksheet, exercise, 
127
 
                 username, date):
 
119
    def __init__(self, req, subject, worksheet, exercise, username, date):
128
120
        # TODO: Find exercise within worksheet.
129
121
        user = ivle.database.User.get_by_login(req.store, username)
130
122
        if user is None:
135
127
        except ValueError:
136
128
            raise NotFound()
137
129
 
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) 
 
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)
161
133
 
162
134
        if attempt is None:
163
135
            raise NotFound()
169
141
        return {'code': self.context.text}
170
142
 
171
143
 
172
 
class WorksheetExerciseRESTView(JSONRESTView):
173
 
    '''REST view of a worksheet exercise.'''
174
 
 
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
 
    @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')
190
155
    def save(self, req, text):
191
 
        # Find the appropriate WorksheetExercise to save to. If its not found,
192
 
        # the user is submitting against a non-existant worksheet/exercise
193
 
 
194
 
        old_save = req.store.find(ExerciseSave,
195
 
            ExerciseSave.ws_ex_id == self.context.id,
196
 
            ExerciseSave.user == req.user).one()
197
 
        
198
 
        #Overwrite the old, or create a new if there isn't one
199
 
        if old_save is None:
200
 
            new_save = ExerciseSave()
201
 
            req.store.add(new_save)
202
 
        else:
203
 
            new_save = old_save
204
 
        
205
 
        new_save.worksheet_exercise = self.context
206
 
        new_save.user = req.user
207
 
        new_save.text = unicode(text)
208
 
        new_save.date = datetime.datetime.now()
209
 
 
210
 
        return {"result": "ok"}
211
 
 
212
 
 
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
 
class WorksheetsRESTView(JSONRESTView):
249
 
    """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')
290
 
    def move_up(self, req, worksheetid):
291
 
        """Takes a list of worksheet-seq_no pairs and updates their 
292
 
        corresponding Worksheet objects to match."""
293
 
        
294
 
        worksheet_below = req.store.find(Worksheet,
295
 
            Worksheet.offering_id == self.context.id,
296
 
            Worksheet.identifier == unicode(worksheetid)).one()
297
 
        if worksheet_below is None:
298
 
            raise NotFound('worksheet_below')
299
 
        worksheet_above = req.store.find(Worksheet,
300
 
            Worksheet.offering_id == self.context.id,
301
 
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
302
 
        if worksheet_above is None:
303
 
            raise NotFound('worksheet_above')
304
 
 
305
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
306
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
307
 
        
308
 
        return {'result': 'ok'}
309
 
 
310
 
    @named_operation('edit')
311
 
    def move_down(self, req, worksheetid):
312
 
        """Takes a list of worksheet-seq_no pairs and updates their 
313
 
        corresponding Worksheet objects to match."""
314
 
        
315
 
        worksheet_above = req.store.find(Worksheet,
316
 
            Worksheet.offering_id == self.context.id,
317
 
            Worksheet.identifier == unicode(worksheetid)).one()
318
 
        if worksheet_above is None:
319
 
            raise NotFound('worksheet_below')
320
 
        worksheet_below = req.store.find(Worksheet,
321
 
            Worksheet.offering_id == self.context.id,
322
 
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
323
 
        if worksheet_below is None:
324
 
            raise NotFound('worksheet_above')
325
 
 
326
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
327
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
328
 
        
329
 
        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"}