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

« back to all changes in this revision

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

  • Committer: stevenbird
  • Date: 2007-12-05 22:41:05 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:7
GPL license

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# IVLE - Informatics Virtual Learning Environment
2
 
# Copyright (C) 2007-2009 The University of Melbourne
3
 
#
4
 
# This program is free software; you can redistribute it and/or modify
5
 
# it under the terms of the GNU General Public License as published by
6
 
# the Free Software Foundation; either version 2 of the License, or
7
 
# (at your option) any later version.
8
 
#
9
 
# This program is distributed in the hope that it will be useful,
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
# GNU General Public License for more details.
13
 
#
14
 
# You should have received a copy of the GNU General Public License
15
 
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
 
 
18
 
# Author: Matt Giuca, Nick Chadwick
19
 
 
20
 
'''AJAX backend for the tutorial application.'''
21
 
 
22
 
import os
23
 
import datetime
24
 
import genshi
25
 
 
26
 
import ivle.util
27
 
import ivle.console
28
 
import ivle.database
29
 
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
30
 
                          Offering, Subject, Semester, WorksheetExercise
31
 
import ivle.worksheet
32
 
import ivle.conf
33
 
import ivle.webapp.tutorial.test
34
 
 
35
 
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
36
 
                                   require_permission)
37
 
from ivle.webapp.errors import NotFound
38
 
 
39
 
# If True, getattempts or getattempt will allow browsing of inactive/disabled
40
 
# attempts. If False, will not allow this.
41
 
HISTORY_ALLOW_INACTIVE = False
42
 
 
43
 
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
44
 
 
45
 
class AttemptsRESTView(JSONRESTView):
46
 
    '''REST view of a user's attempts at an exercise.'''
47
 
    
48
 
    def __init__(self, req, subject, year, semester, worksheet, 
49
 
                                                exercise, username):
50
 
        self.user = ivle.database.User.get_by_login(req.store, username)
51
 
        if self.user is None:
52
 
            raise NotFound()
53
 
        
54
 
        self.worksheet_exercise = req.store.find(WorksheetExercise,
55
 
            WorksheetExercise.exercise_id == exercise,
56
 
            WorksheetExercise.worksheet_id == Worksheet.id,
57
 
            Worksheet.offering_id == Offering.id,
58
 
            Offering.subject_id == Subject.id,
59
 
            Subject.short_name == subject,
60
 
            Offering.semester_id == Semester.id,
61
 
            Semester.year == year,
62
 
            Semester.semester == semester).one()
63
 
        
64
 
        self.context = self.user # XXX: Not quite right.
65
 
 
66
 
    @require_permission('edit')
67
 
    def GET(self, req):
68
 
        """Handles a GET Attempts action."""
69
 
        attempts = req.store.find(ExerciseAttempt, 
70
 
                ExerciseAttempt.ws_ex_id == self.worksheet_exercise.id,
71
 
                ExerciseAttempt.user_id == self.user.id)
72
 
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
73
 
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
74
 
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
75
 
                for a in attempts]
76
 
 
77
 
        return attempts
78
 
 
79
 
 
80
 
    @require_permission('edit')
81
 
    def PUT(self, req, data):
82
 
        """ Tests the given submission """
83
 
        exercise = req.store.find(Exercise, 
84
 
            Exercise.id == self.worksheet_exercise.exercise_id).one()
85
 
        if exercise is None:
86
 
            raise NotFound()
87
 
 
88
 
        # Start a console to run the tests on
89
 
        jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
90
 
        working_dir = os.path.join("/home", req.user.login)
91
 
        cons = ivle.console.Console(req.user.unixid, jail_path, 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()
103
 
 
104
 
        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
 
        )
110
 
 
111
 
        req.store.add(attempt)
112
 
 
113
 
        # Query the DB to get an updated score on whether or not this problem
114
 
        # has EVER been completed (may be different from "passed", if it has
115
 
        # been completed before), and the total number of attempts.
116
 
        completed, attempts = ivle.worksheet.get_exercise_status(req.store,
117
 
            req.user, self.worksheet_exercise)
118
 
        test_results["completed"] = completed
119
 
        test_results["attempts"] = attempts
120
 
 
121
 
        return test_results
122
 
 
123
 
 
124
 
class AttemptRESTView(JSONRESTView):
125
 
    '''REST view of an exercise attempt.'''
126
 
 
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.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
 
    @require_permission('view')
169
 
    def GET(self, req):
170
 
        return {'code': self.context.text}
171
 
 
172
 
 
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')
191
 
    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
 
 
215
 
# Note that this is the view of an existing worksheet. Creation is handled
216
 
# by OfferingRESTView (as offerings have worksheets)
217
 
class WorksheetRESTView(JSONRESTView):
218
 
    """View used to update a worksheet."""
219
 
 
220
 
    def __init__(self, req, **kwargs):
221
 
    
222
 
        self.worksheet = kwargs['worksheet']
223
 
        self.subject = kwargs['subject']
224
 
        self.year = kwargs['year']
225
 
        self.semester = kwargs['semester']
226
 
    
227
 
        self.context = req.store.find(Worksheet,
228
 
            Worksheet.identifier == self.worksheet,
229
 
            Worksheet.offering_id == Offering.id,
230
 
            Offering.subject_id == Subject.id,
231
 
            Subject.short_name == self.subject,
232
 
            Offering.semester_id == Semester.id,
233
 
            Semester.year == self.year,
234
 
            Semester.semester == self.semester).one()
235
 
        
236
 
        if self.context is None:
237
 
            raise NotFound()
238
 
    
239
 
    @named_operation('edit')
240
 
    def save(self, req, name, assessable, data, format):
241
 
        """Takes worksheet data and saves it."""
242
 
        self.context.name = unicode(name)
243
 
        self.context.assessable = self.convert_bool(assessable)
244
 
        self.context.data = unicode(data)
245
 
        self.context.format = unicode(format)
246
 
        ivle.worksheet.update_exerciselist(self.context)
247
 
        
248
 
        return {"result": "ok"}
249
 
 
250
 
class WorksheetsRESTView(JSONRESTView):
251
 
    """View used to update and create Worksheets."""
252
 
    
253
 
    def __init__(self, req, **kwargs):
254
 
    
255
 
        self.subject = kwargs['subject']
256
 
        self.year = kwargs['year']
257
 
        self.semester = kwargs['semester']
258
 
    
259
 
        self.context = req.store.find(Offering,
260
 
            Offering.subject_id == Subject.id,
261
 
            Subject.short_name == self.subject,
262
 
            Offering.semester_id == Semester.id,
263
 
            Semester.year == self.year,
264
 
            Semester.semester == self.semester).one()
265
 
        
266
 
        if self.context is None:
267
 
            raise NotFound()
268
 
 
269
 
    @named_operation('edit')
270
 
    def add_worksheet(self, req, identifier, name, assessable, data, format):
271
 
        """Takes worksheet data and adds it."""
272
 
        
273
 
        new_worksheet = Worksheet()
274
 
        new_worksheet.seq_no = self.context.worksheets.count()
275
 
        # Setting new_worksheet.offering implicitly adds new_worksheet,
276
 
        # hence worksheets.count MUST be called above it
277
 
        new_worksheet.offering = self.context
278
 
        new_worksheet.identifier = unicode(identifier)
279
 
        new_worksheet.name = unicode(name)
280
 
        new_worksheet.assessable = self.convert_bool(assessable)
281
 
        new_worksheet.data = unicode(data)
282
 
        new_worksheet.format = unicode(format)
283
 
        
284
 
        # This call is added for clarity, as the worksheet is implicitly added.        
285
 
        req.store.add(new_worksheet)
286
 
        
287
 
        ivle.worksheet.update_exerciselist(new_worksheet)
288
 
        
289
 
        return {"result": "ok"}
290
 
 
291
 
    @named_operation('edit')
292
 
    def move_up(self, req, worksheetid):
293
 
        """Takes a list of worksheet-seq_no pairs and updates their 
294
 
        corresponding Worksheet objects to match."""
295
 
        
296
 
        worksheet_below = req.store.find(Worksheet,
297
 
            Worksheet.offering_id == self.context.id,
298
 
            Worksheet.identifier == unicode(worksheetid)).one()
299
 
        if worksheet_below is None:
300
 
            raise NotFound('worksheet_below')
301
 
        worksheet_above = req.store.find(Worksheet,
302
 
            Worksheet.offering_id == self.context.id,
303
 
            Worksheet.seq_no == (worksheet_below.seq_no - 1)).one()
304
 
        if worksheet_above is None:
305
 
            raise NotFound('worksheet_above')
306
 
 
307
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
308
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
309
 
        
310
 
        return {'result': 'ok'}
311
 
 
312
 
    @named_operation('edit')
313
 
    def move_down(self, req, worksheetid):
314
 
        """Takes a list of worksheet-seq_no pairs and updates their 
315
 
        corresponding Worksheet objects to match."""
316
 
        
317
 
        worksheet_above = req.store.find(Worksheet,
318
 
            Worksheet.offering_id == self.context.id,
319
 
            Worksheet.identifier == unicode(worksheetid)).one()
320
 
        if worksheet_above is None:
321
 
            raise NotFound('worksheet_below')
322
 
        worksheet_below = req.store.find(Worksheet,
323
 
            Worksheet.offering_id == self.context.id,
324
 
            Worksheet.seq_no == (worksheet_above.seq_no + 1)).one()
325
 
        if worksheet_below is None:
326
 
            raise NotFound('worksheet_above')
327
 
 
328
 
        worksheet_below.seq_no = worksheet_below.seq_no - 1
329
 
        worksheet_above.seq_no = worksheet_above.seq_no + 1
330
 
        
331
 
        return {'result': 'ok'}