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

1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
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: Nick Chadwick
19
20
21
import ivle.database
22
from ivle.database import Exercise, TestSuite, TestCase, \
23
                          TestSuiteVar, TestCasePart
24
from ivle.webapp.base.rest import (JSONRESTView, named_operation,
25
                                   require_permission)
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
26
from ivle.webapp.errors import NotFound, BadRequest
1413 by William Grant
Display TestErrors in test mode, much like TestCreationErrors.
27
from ivle.webapp.tutorial.test.TestFramework import (
28
    TestCreationError, TestError)
1394.2.9 by William Grant
Actually test exercise tests! It works!
29
from ivle.worksheet.utils import test_exercise_submission
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
30
31
32
class ExercisesRESTView(JSONRESTView):
33
    """Rest view for adding an exercise."""
34
    
1315 by William Grant
Allow tutors to add exercises.
35
    #Only lecturers, tutors and admins can add exercises
1544 by Matt Giuca
Added an argument 'config' to every single get_permissions method throughout the program. All calls to get_permissions pass a config. This is to allow per-site policy configurations on permissions.
36
    def get_permissions(self, user, config):
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
37
        if user is not None:
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
38
            if user.admin:
39
                return set(['save'])
1099.1.237 by Nick Chadwick
Updated exercise_service to make use of the new deletion methods
40
            elif 'lecturer' in set((e.role for e in user.active_enrolments)):
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
41
                return set(['save'])
1315 by William Grant
Allow tutors to add exercises.
42
            elif 'tutor' in set((e.role for e in user.active_enrolments)):
43
                return set(['save'])
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
44
            else:
45
                return set()
46
        else:
47
            return set()
48
    
49
    @named_operation('save')
50
    def add_exercise(self, req, identifier, name, description, partial, solution, include, num_rows):
51
    
52
        new_exercise = Exercise()
53
        new_exercise.id = unicode(identifier)
1099.1.229 by Nick Chadwick
Fixed a slight oversight, which meant there was no way to add a new
54
        new_exercise.name = unicode(name)
55
        new_exercise.description = unicode(description)
56
        new_exercise.partial = unicode(partial)
57
        new_exercise.solution = unicode(solution)
58
        new_exercise.include = unicode(include)
59
        new_exercise.num_rows = int(num_rows)
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
60
        
61
        req.store.add(new_exercise)
62
        
63
        return {'result': 'ok'}
64
        
65
        
66
67
class ExerciseRESTView(JSONRESTView):
68
    """View for updating Exercises"""
69
    
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
70
    @named_operation(u'edit')
1099.1.217 by Nick Chadwick
working on making the exercise editor complete
71
    def edit_exercise(self, req, name, description, partial, 
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
72
                      solution, include, num_rows):
73
        
74
        self.context.name = unicode(name)
75
        self.context.description = unicode(description)
76
        self.context.partial = unicode(partial)
77
        self.context.solution = unicode(solution)
78
        self.context.include = unicode(include)
79
        self.context.num_rows = int(num_rows)
1099.1.237 by Nick Chadwick
Updated exercise_service to make use of the new deletion methods
80
        return {'result': 'ok'}
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
81
    
82
    @named_operation(u'edit')
83
    def delete_exercise(self, req, id):
84
        
1099.1.242 by Nick Chadwick
Fixed a problem with exercise editor, which wasn't editing or adding
85
        self.context.delete()
86
        return {'result': 'ok'}
87
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
88
    @named_operation(u'edit')
1099.1.217 by Nick Chadwick
working on making the exercise editor complete
89
    def add_suite(self, req, description, function, stdin):
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
90
        
91
        new_suite = TestSuite()
1099.1.217 by Nick Chadwick
working on making the exercise editor complete
92
        new_suite.description = unicode(description)
93
        new_suite.seq_no = self.context.test_suites.count()
94
        new_suite.function = unicode(function)
95
        new_suite.stdin = unicode(stdin)
1099.1.216 by Nick Chadwick
Started adding in add and save options in the exercise edit view, to
96
        new_suite.exercise = self.context
97
        
98
        req.store.add(new_suite)
99
        
100
        return {'result': 'ok'}
101
        
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
102
    @named_operation(u'edit')
1099.1.217 by Nick Chadwick
working on making the exercise editor complete
103
    def edit_suite(self, req, suiteid, description, function, stdin):
104
        
105
        suite = req.store.find(TestSuite,
106
            TestSuite.suiteid == int(suiteid),
107
            TestSuite.exercise_id == self.context.id).one()
108
        
109
        if suite is None:
110
            raise NotFound()
111
        
112
        suite.description = unicode(description)
113
        suite.function = unicode(function)
114
        suite.stdin = unicode(stdin)
115
        
116
        return {'result': 'ok'}
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
117
    
118
    @named_operation(u'edit')
119
    def delete_suite(self, req, suiteid):
120
        
121
        suite = req.store.find(TestSuite,
122
            TestSuite.suiteid == int(suiteid),
123
            TestSuite.exercise_id == self.context.id).one()
124
        if suite is None:
125
            raise NotFound()
126
        
1099.1.242 by Nick Chadwick
Fixed a problem with exercise editor, which wasn't editing or adding
127
        suite.delete()
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
128
        
129
        return {'result': 'ok'}
1099.1.217 by Nick Chadwick
working on making the exercise editor complete
130
      
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
131
    @named_operation(u'edit')
1099.1.217 by Nick Chadwick
working on making the exercise editor complete
132
    def add_var(self, req, suiteid, var_type, var_name, var_val, argno):
133
134
        suite = req.store.find(TestSuite,
135
            TestSuite.suiteid == int(suiteid),
136
            TestSuite.exercise_id == self.context.id).one()
137
        
138
        if suite is None:
139
            raise NotFound()
140
        
141
        new_var = TestSuiteVar()
142
        new_var.var_type = unicode(var_type)
143
        new_var.var_name = unicode(var_name)
1409 by William Grant
Unbreak variable adding and editing.
144
        new_var.var_value = unicode(var_val)
145
        new_var.arg_no = int(argno) if len(argno) else None
1099.1.217 by Nick Chadwick
working on making the exercise editor complete
146
        new_var.suite = suite
147
        
148
        req.store.add(new_var)
149
        
150
        return {'result': 'ok'}
1099.1.220 by Nick Chadwick
Merged from trunk
151
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
152
    @named_operation(u'edit')
1099.1.220 by Nick Chadwick
Merged from trunk
153
    def edit_var(self, req, suiteid, varid, var_type, var_name, var_val, argno):
154
        var = req.store.find(TestSuiteVar,
155
            TestSuiteVar.varid == int(varid),
156
            TestSuiteVar.suiteid == int(suiteid)
157
        ).one()
158
        
159
        if var is None:
1099.1.242 by Nick Chadwick
Fixed a problem with exercise editor, which wasn't editing or adding
160
            raise NotFound("Var not found.")
1099.1.220 by Nick Chadwick
Merged from trunk
161
            
162
        var.var_type = unicode(var_type)
163
        var.var_name = unicode(var_name)
1409 by William Grant
Unbreak variable adding and editing.
164
        var.var_value = unicode(var_val)
165
        var.arg_no = int(argno) if len(argno) else None
1099.1.220 by Nick Chadwick
Merged from trunk
166
        
167
        return {'result': 'ok'}
168
    
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
169
    @named_operation(u'edit')
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
170
    def delete_var(self, req, suiteid, varid):
171
        var = req.store.find(TestSuiteVar,
172
            TestSuiteVar.varid == int(varid),
173
            TestSuiteVar.suiteid == int(suiteid)).one()
174
        if var is None:
175
            raise NotFound()
176
        
1099.1.242 by Nick Chadwick
Fixed a problem with exercise editor, which wasn't editing or adding
177
        var.delete()
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
178
        
179
        return {'result': 'ok'}
1099.1.237 by Nick Chadwick
Updated exercise_service to make use of the new deletion methods
180
        
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
181
    @named_operation(u'edit')
1394.1.5 by William Grant
Drop TestSuite file match default from the UI -- we don't support file tests any more.
182
    def add_testcase(self, req, suiteid, passmsg, failmsg):
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
183
        
184
        suite = req.store.find(TestSuite,
185
            TestSuite.suiteid == int(suiteid),
186
            TestSuite.exercise_id == self.context.id).one()
187
        
188
        if suite is None:
189
            raise NotFound()
190
        
191
        new_case = TestCase()
192
        new_case.passmsg = unicode(passmsg)
193
        new_case.failmsg = unicode(failmsg)
194
        new_case.seq_no = suite.test_cases.count()
1412 by William Grant
Disable support for testcases with a default other than 'ignore'.
195
        # XXX: Force this for now, since we don't support the
196
        #      'match' default. It might make sense to support
197
        #      this again once file testing support returns.
198
        new_case.test_default = u'ignore'
1099.1.221 by Nick Chadwick
added in extra parts to the exercise edit view. Now almost all
199
        suite.test_cases.add(new_case)
200
        
201
        req.store.add(new_case)
202
        
203
        return {'result': 'ok'}
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
204
    
205
    @named_operation(u'edit')
1394.1.5 by William Grant
Drop TestSuite file match default from the UI -- we don't support file tests any more.
206
    def edit_testcase(self, req, suiteid, testid, passmsg, failmsg):
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
207
        
208
        suite = req.store.find(TestSuite,
209
            TestSuite.suiteid == int(suiteid),
210
            TestSuite.exercise_id == self.context.id).one()
211
        if suite is None:
1099.1.238 by Nick Chadwick
Removed some extraneous output in exercise REST views, when encountering
212
            raise NotFound()
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
213
        
214
        test_case = req.store.find(TestCase,
215
            TestCase.suiteid == suite.suiteid,
216
            TestCase.testid == int(testid)).one()
217
        if test_case is None:
1099.1.238 by Nick Chadwick
Removed some extraneous output in exercise REST views, when encountering
218
            raise NotFound()
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
219
        
220
        test_case.passmsg = unicode(passmsg)
221
        test_case.failmsg = unicode(failmsg)
222
        
223
        return {'result': 'ok'}
224
    
225
    @named_operation(u'edit')
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
226
    def delete_testcase(self, req, suiteid, testid):
227
        
228
        suite = req.store.find(TestSuite,
229
            TestSuite.suiteid == int(suiteid),
230
            TestSuite.exercise_id == self.context.id).one()
231
        if suite is None:
232
            raise NotFound()
233
        
234
        test_case = req.store.find(TestCase,
235
            TestCase.suiteid == suite.suiteid,
236
            TestCase.testid == int(testid)).one()
1099.6.4 by Nick Chadwick
Exercise UI is now ready to be merged into trunk.
237
        if test_case is None:   
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
238
            raise NotFound()
1099.1.237 by Nick Chadwick
Updated exercise_service to make use of the new deletion methods
239
1099.1.242 by Nick Chadwick
Fixed a problem with exercise editor, which wasn't editing or adding
240
        test_case.delete()
1099.1.237 by Nick Chadwick
Updated exercise_service to make use of the new deletion methods
241
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
242
        return {'result': 'ok'}
243
    
244
    @named_operation(u'edit')
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
245
    def edit_testpart(self, req, suiteid, testid, partid, part_type, test_type, 
1394.1.7 by William Grant
Drop TestCasePart filename UI.
246
                      data):
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
247
    
248
        suite = req.store.find(TestSuite,
249
            TestSuite.suiteid == int(suiteid),
250
            TestSuite.exercise_id == self.context.id).one()
251
        if suite is None:
1099.1.238 by Nick Chadwick
Removed some extraneous output in exercise REST views, when encountering
252
            raise NotFound()
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
253
        
254
        test_case = req.store.find(TestCase,
255
            TestCase.suiteid == suite.suiteid,
256
            TestCase.testid == int(testid)).one()
257
        if test_case is None:
1099.1.238 by Nick Chadwick
Removed some extraneous output in exercise REST views, when encountering
258
            raise NotFound()
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
259
        
260
        test_part = req.store.find(TestCasePart,
261
            TestCasePart.testid == test_case.testid,
262
            TestCasePart.partid == int(partid)).one()
263
        if test_part is None:
264
            raise NotFound('testcasepart')
265
        
266
        test_part.part_type = unicode(part_type)
267
        test_part.test_type = unicode(test_type)
268
        test_part.data = unicode(data)
269
        
270
        return {'result': 'ok'}
271
    
272
    @named_operation(u'edit')
273
    def add_testpart(self, req, suiteid, testid, part_type, test_type, 
1394.1.7 by William Grant
Drop TestCasePart filename UI.
274
                      data):
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
275
    
276
        suite = req.store.find(TestSuite,
277
            TestSuite.suiteid == int(suiteid),
278
            TestSuite.exercise_id == self.context.id).one()
279
        if suite is None:
1099.1.238 by Nick Chadwick
Removed some extraneous output in exercise REST views, when encountering
280
            raise NotFound()
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
281
        
282
        test_case = req.store.find(TestCase,
283
            TestCase.suiteid == suite.suiteid,
284
            TestCase.testid == int(testid)).one()
285
        if test_case is None:
1099.1.238 by Nick Chadwick
Removed some extraneous output in exercise REST views, when encountering
286
            raise NotFound()
1099.6.1 by Nick Chadwick
Exercise-ui is now able to create an entire exercise.
287
        
288
        test_part = TestCasePart()
289
        test_part.part_type = unicode(part_type)
290
        test_part.test_type = unicode(test_type)
291
        test_part.data = unicode(data)
292
        
293
        test_case.parts.add(test_part)
294
        
295
        return {'result': 'ok'}
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
296
    
297
    @named_operation(u'edit')
298
    def delete_testpart(self, req, suiteid, testid, partid):
299
        suite = req.store.find(TestSuite,
300
            TestSuite.suiteid == int(suiteid),
301
            TestSuite.exercise_id == self.context.id).one()
302
        if suite is None:
303
            raise NotFound()
304
        
305
        test_case = req.store.find(TestCase,
306
            TestCase.suiteid == suite.suiteid,
307
            TestCase.testid == int(testid)).one()
308
        if test_case is None:
309
            raise NotFound()
310
        
311
        test_part = req.store.find(TestCasePart,
312
            TestCasePart.testid == test_case.testid,
313
            TestCasePart.partid == int(partid)).one()
314
        if test_part is None:
1099.1.238 by Nick Chadwick
Removed some extraneous output in exercise REST views, when encountering
315
            raise NotFound()
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
316
        
1099.1.242 by Nick Chadwick
Fixed a problem with exercise editor, which wasn't editing or adding
317
        test_part.delete()
1099.6.3 by Nick Chadwick
Edited the exercise service to delete individual parts of an exercise.
318
        
319
        return {'result': 'ok'}
1394.2.7 by William Grant
Hook up a new ExerciseRESTView.test into the JS.
320
321
    @named_operation(u'edit')
322
    def test(self, req, code):
1397.1.1 by William Grant
Return a TestCreationError if one occurs while testing an exercise standalone.
323
        try:
324
            return test_exercise_submission(
325
                req.config, req.user, self.context, code)
326
        except TestCreationError, e:
1397.1.5 by William Grant
Report TestCreationErrors as a critical error during testing.
327
            return {'critical_error': {'name': 'TestCreationError', 'detail': e._reason}}
1413 by William Grant
Display TestErrors in test mode, much like TestCreationErrors.
328
        except TestError, e:
329
            return {'critical_error': {'name': 'TestError', 'detail': str(e)}}