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