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

« back to all changes in this revision

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

  • Committer: drtomc
  • Date: 2007-12-04 01:57:41 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:3
A README file describing sundry bits of the platform infrastructure.

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.console
27
 
import ivle.database
28
 
from ivle.database import Exercise, ExerciseAttempt, ExerciseSave, Worksheet, \
29
 
                          Offering, Subject, Semester, WorksheetExercise
30
 
import ivle.worksheet.utils
31
 
import ivle.webapp.tutorial.test
32
 
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
33
 
                                   require_permission)
34
 
from ivle.webapp.errors import NotFound
35
 
 
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
 
 
40
 
TIMESTAMP_FORMAT = '%Y-%m-%d %H:%M:%S'
41
 
 
42
 
class AttemptsRESTView(JSONRESTView):
43
 
    '''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
 
 
64
 
    @require_permission('edit')
65
 
    def GET(self, req):
66
 
        """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)
70
 
        # attempts is a list of ExerciseAttempt objects. Convert to dictionaries
71
 
        time_fmt = lambda dt: datetime.datetime.strftime(dt, TIMESTAMP_FORMAT)
72
 
        attempts = [{'date': time_fmt(a.date), 'complete': a.complete}
73
 
                for a in attempts]
74
 
 
75
 
        return attempts
76
 
 
77
 
 
78
 
    @require_permission('edit')
79
 
    def PUT(self, req, data):
80
 
        """ 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()
102
 
 
103
 
        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
 
        )
109
 
 
110
 
        req.store.add(attempt)
111
 
 
112
 
        # Query the DB to get an updated score on whether or not this problem
113
 
        # has EVER been completed (may be different from "passed", if it has
114
 
        # 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)
117
 
        test_results["completed"] = completed
118
 
        test_results["attempts"] = attempts
119
 
 
120
 
        return test_results
121
 
 
122
 
 
123
 
class AttemptRESTView(JSONRESTView):
124
 
    '''REST view of an exercise attempt.'''
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
 
        # 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
 
    @require_permission('view')
168
 
    def GET(self, req):
169
 
        return {'code': self.context.text}
170
 
 
171
 
 
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')
190
 
    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'}