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

« back to all changes in this revision

Viewing changes to ivle/webapp/admin/subject.py

  • Committer: David Coles
  • Date: 2010-02-13 00:59:09 UTC
  • Revision ID: coles.david@gmail.com-20100213005909-ecl1nd4g0criri21
docs: Don't show stubbed end user documentation. It looks bad, especially when some of it has lurked for years\!

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
import formencode
36
36
import formencode.validators
37
37
 
 
38
from ivle.webapp.base.forms import BaseFormView
 
39
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
38
40
from ivle.webapp.base.xhtml import XHTMLView
39
 
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
40
41
from ivle.webapp import ApplicationRoot
41
42
 
42
43
from ivle.database import Subject, Semester, Offering, Enrolment, User,\
113
114
    code = formencode.validators.UnicodeString(not_empty=True)
114
115
 
115
116
 
116
 
class SubjectFormView(XHTMLView):
 
117
class SubjectFormView(BaseFormView):
117
118
    """An abstract form to add or edit a subject."""
118
119
    tab = 'subjects'
119
120
 
120
121
    def authorize(self, req):
121
122
        return req.user is not None and req.user.admin
122
123
 
123
 
    def filter(self, stream, ctx):
124
 
        return stream | HTMLFormFiller(data=ctx['data'])
125
 
 
126
124
    def populate_state(self, state):
127
125
        state.existing_subject = None
128
126
 
129
 
    def populate(self, req, ctx):
130
 
        if req.method == 'POST':
131
 
            data = dict(req.get_fieldstorage())
132
 
            try:
133
 
                validator = SubjectSchema()
134
 
                self.populate_state(req)
135
 
                data = validator.to_python(data, state=req)
136
 
 
137
 
                subject = self.update_subject_object(req, data)
138
 
 
139
 
                req.store.commit()
140
 
                req.throw_redirect(req.publisher.generate(subject))
141
 
            except formencode.Invalid, e:
142
 
                errors = e.unpack_errors()
143
 
        else:
144
 
            data = self.get_default_data(req)
145
 
            errors = {}
146
 
 
147
 
        if errors:
148
 
            req.store.rollback()
149
 
 
150
 
        ctx['context'] = self.context
151
 
        ctx['data'] = data or {}
152
 
        ctx['errors'] = errors
 
127
    @property
 
128
    def validator(self):
 
129
        return SubjectSchema()
 
130
 
 
131
    def get_return_url(self, obj):
 
132
        return '/subjects'
153
133
 
154
134
 
155
135
class SubjectNew(SubjectFormView):
156
136
    """A form to create a subject."""
157
137
    template = 'templates/subject-new.html'
158
138
 
159
 
    def populate_state(self, state):
160
 
        state.existing_subject = self.context
161
 
 
162
139
    def get_default_data(self, req):
163
140
        return {}
164
141
 
165
 
    def update_subject_object(self, req, data):
 
142
    def save_object(self, req, data):
166
143
        new_subject = Subject()
167
144
        new_subject.short_name = data['short_name']
168
145
        new_subject.name = data['name']
186
163
            'code': self.context.code,
187
164
            }
188
165
 
189
 
    def update_subject_object(self, req, data):
 
166
    def save_object(self, req, data):
190
167
        self.context.short_name = data['short_name']
191
168
        self.context.name = data['name']
192
169
        self.context.code = data['code']
194
171
        return self.context
195
172
 
196
173
 
 
174
class SemesterUniquenessValidator(formencode.FancyValidator):
 
175
    """A FormEncode validator that checks that a semester is unique.
 
176
 
 
177
    There cannot be more than one semester for the same year and semester.
 
178
    """
 
179
    def _to_python(self, value, state):
 
180
        if (state.store.find(
 
181
                Semester, year=value['year'], semester=value['semester']
 
182
                ).count() > 0):
 
183
            raise formencode.Invalid(
 
184
                'Semester already exists', value, state)
 
185
        return value
 
186
 
 
187
 
 
188
class SemesterSchema(formencode.Schema):
 
189
    year = formencode.validators.UnicodeString()
 
190
    semester = formencode.validators.UnicodeString()
 
191
    chained_validators = [SemesterUniquenessValidator()]
 
192
 
 
193
 
 
194
class SemesterNew(BaseFormView):
 
195
    """A form to create a semester."""
 
196
    template = 'templates/semester-new.html'
 
197
    tab = 'subjects'
 
198
 
 
199
    def authorize(self, req):
 
200
        return req.user is not None and req.user.admin
 
201
 
 
202
    @property
 
203
    def validator(self):
 
204
        return SemesterSchema()
 
205
 
 
206
    def get_default_data(self, req):
 
207
        return {}
 
208
 
 
209
    def save_object(self, req, data):
 
210
        new_semester = Semester()
 
211
        new_semester.year = data['year']
 
212
        new_semester.semester = data['semester']
 
213
 
 
214
        req.store.add(new_semester)
 
215
        return new_semester
 
216
 
 
217
    def get_return_url(self, obj):
 
218
        return '/subjects'
 
219
 
 
220
 
197
221
class OfferingView(XHTMLView):
198
222
    """The home page of an offering."""
199
223
    template = 'templates/offering.html'
205
229
        self.plugin_styles[TutorialPlugin] = ['tutorial.css']
206
230
        ctx['context'] = self.context
207
231
        ctx['req'] = req
208
 
        ctx['permissions'] = self.context.get_permissions(req.user)
 
232
        ctx['permissions'] = self.context.get_permissions(req.user,req.config)
209
233
        ctx['format_submission_principal'] = util.format_submission_principal
210
234
        ctx['format_datetime'] = ivle.date.make_date_nice
211
235
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
212
236
        ctx['OfferingEdit'] = OfferingEdit
 
237
        ctx['GroupsView'] = GroupsView
213
238
 
214
239
        # As we go, calculate the total score for this subject
215
240
        # (Assessable worksheets only, mandatory problems only)
234
259
                    problems_done, problems_total))
235
260
 
236
261
 
 
262
class SubjectValidator(formencode.FancyValidator):
 
263
    """A FormEncode validator that turns a subject name into a subject.
 
264
 
 
265
    The state must have a 'store' attribute, which is the Storm store
 
266
    to use.
 
267
    """
 
268
    def _to_python(self, value, state):
 
269
        subject = state.store.find(Subject, short_name=value).one()
 
270
        if subject:
 
271
            return subject
 
272
        else:
 
273
            raise formencode.Invalid('Subject does not exist', value, state)
 
274
 
 
275
 
 
276
class SemesterValidator(formencode.FancyValidator):
 
277
    """A FormEncode validator that turns a string into a semester.
 
278
 
 
279
    The string should be of the form 'year/semester', eg. '2009/1'.
 
280
 
 
281
    The state must have a 'store' attribute, which is the Storm store
 
282
    to use.
 
283
    """
 
284
    def _to_python(self, value, state):
 
285
        try:
 
286
            year, semester = value.split('/')
 
287
        except ValueError:
 
288
            year = semester = None
 
289
 
 
290
        semester = state.store.find(
 
291
            Semester, year=year, semester=semester).one()
 
292
        if semester:
 
293
            return semester
 
294
        else:
 
295
            raise formencode.Invalid('Semester does not exist', value, state)
 
296
 
 
297
 
 
298
class OfferingUniquenessValidator(formencode.FancyValidator):
 
299
    """A FormEncode validator that checks that an offering is unique.
 
300
 
 
301
    There cannot be more than one offering in the same year and semester.
 
302
 
 
303
    The offering referenced by state.existing_offering is permitted to
 
304
    hold that year and semester tuple. If any other object holds it, the
 
305
    input is rejected.
 
306
    """
 
307
    def _to_python(self, value, state):
 
308
        if (state.store.find(
 
309
                Offering, subject=value['subject'],
 
310
                semester=value['semester']).one() not in
 
311
                (None, state.existing_offering)):
 
312
            raise formencode.Invalid(
 
313
                'Offering already exists', value, state)
 
314
        return value
 
315
 
 
316
 
237
317
class OfferingSchema(formencode.Schema):
238
318
    description = formencode.validators.UnicodeString(
239
319
        if_missing=None, not_empty=False)
240
320
    url = formencode.validators.URL(if_missing=None, not_empty=False)
241
321
 
242
322
 
243
 
class OfferingEdit(XHTMLView):
 
323
class OfferingAdminSchema(OfferingSchema):
 
324
    subject = formencode.All(
 
325
        SubjectValidator(), formencode.validators.UnicodeString())
 
326
    semester = formencode.All(
 
327
        SemesterValidator(), formencode.validators.UnicodeString())
 
328
    chained_validators = [OfferingUniquenessValidator()]
 
329
 
 
330
 
 
331
class OfferingEdit(BaseFormView):
244
332
    """A form to edit an offering's details."""
245
333
    template = 'templates/offering-edit.html'
246
334
    tab = 'subjects'
247
335
    permission = 'edit'
248
336
 
249
 
    def filter(self, stream, ctx):
250
 
        return stream | HTMLFormFiller(data=ctx['data'])
251
 
 
252
 
    def populate(self, req, ctx):
253
 
        if req.method == 'POST':
254
 
            data = dict(req.get_fieldstorage())
255
 
            try:
256
 
                validator = OfferingSchema()
257
 
                data = validator.to_python(data, state=req)
258
 
 
259
 
                self.context.url = unicode(data['url']) if data['url'] else None
260
 
                self.context.description = data['description']
261
 
                req.store.commit()
262
 
                req.throw_redirect(req.publisher.generate(self.context))
263
 
            except formencode.Invalid, e:
264
 
                errors = e.unpack_errors()
 
337
    @property
 
338
    def validator(self):
 
339
        if self.req.user.admin:
 
340
            return OfferingAdminSchema()
265
341
        else:
266
 
            data = {
267
 
                'url': self.context.url,
268
 
                'description': self.context.description,
 
342
            return OfferingSchema()
 
343
 
 
344
    def populate(self, req, ctx):
 
345
        super(OfferingEdit, self).populate(req, ctx)
 
346
        ctx['subjects'] = req.store.find(Subject)
 
347
        ctx['semesters'] = req.store.find(Semester)
 
348
 
 
349
    def populate_state(self, state):
 
350
        state.existing_offering = self.context
 
351
 
 
352
    def get_default_data(self, req):
 
353
        return {
 
354
            'subject': self.context.subject.short_name,
 
355
            'semester': self.context.semester.year + '/' +
 
356
                        self.context.semester.semester,
 
357
            'url': self.context.url,
 
358
            'description': self.context.description,
269
359
            }
270
 
            errors = {}
271
 
 
272
 
        ctx['data'] = data or {}
273
 
        ctx['context'] = self.context
274
 
        ctx['errors'] = errors
 
360
 
 
361
    def save_object(self, req, data):
 
362
        if req.user.admin:
 
363
            self.context.subject = data['subject']
 
364
            self.context.semester = data['semester']
 
365
        self.context.description = data['description']
 
366
        self.context.url = unicode(data['url']) if data['url'] else None
 
367
        return self.context
 
368
 
 
369
 
 
370
class OfferingNew(BaseFormView):
 
371
    """A form to create an offering."""
 
372
    template = 'templates/offering-new.html'
 
373
    tab = 'subjects'
 
374
 
 
375
    def authorize(self, req):
 
376
        return req.user is not None and req.user.admin
 
377
 
 
378
    @property
 
379
    def validator(self):
 
380
        return OfferingAdminSchema()
 
381
 
 
382
    def populate(self, req, ctx):
 
383
        super(OfferingNew, self).populate(req, ctx)
 
384
        ctx['subjects'] = req.store.find(Subject)
 
385
        ctx['semesters'] = req.store.find(Semester)
 
386
 
 
387
    def populate_state(self, state):
 
388
        state.existing_offering = None
 
389
 
 
390
    def get_default_data(self, req):
 
391
        return {}
 
392
 
 
393
    def save_object(self, req, data):
 
394
        new_offering = Offering()
 
395
        new_offering.subject = data['subject']
 
396
        new_offering.semester = data['semester']
 
397
        new_offering.description = data['description']
 
398
        new_offering.url = unicode(data['url']) if data['url'] else None
 
399
 
 
400
        req.store.add(new_offering)
 
401
        return new_offering
275
402
 
276
403
 
277
404
class UserValidator(formencode.FancyValidator):
305
432
    The state must have an 'offering' attribute.
306
433
    """
307
434
    def _to_python(self, value, state):
308
 
        if ("enrol_" + value) not in state.offering.get_permissions(state.user):
 
435
        if (("enrol_" + value) not in
 
436
                state.offering.get_permissions(state.user, state.config)):
309
437
            raise formencode.Invalid('Not allowed to assign users that role',
310
438
                                     value, state)
311
439
        return value
355
483
 
356
484
        ctx['data'] = data or {}
357
485
        ctx['offering'] = self.context
358
 
        ctx['roles_auth'] = self.context.get_permissions(req.user)
 
486
        ctx['roles_auth'] = self.context.get_permissions(req.user, req.config)
359
487
        ctx['errors'] = errors
360
488
 
361
489
class OfferingProjectsView(XHTMLView):
406
534
class ProjectView(XHTMLView):
407
535
    """View the submissions for a ProjectSet"""
408
536
    template = "templates/project.html"
409
 
    permission = "edit"
 
537
    permission = "view_project_submissions"
410
538
    tab = 'subjects'
411
539
 
412
540
    def build_subversion_url(self, svnroot, submission):
446
574
 
447
575
    views = [(ApplicationRoot, ('subjects', '+index'), SubjectsView),
448
576
             (ApplicationRoot, ('subjects', '+new'), SubjectNew),
 
577
             (ApplicationRoot, ('subjects', '+new-offering'), OfferingNew),
 
578
             (ApplicationRoot, ('subjects', '+new-semester'), SemesterNew),
449
579
             (Subject, '+edit', SubjectEdit),
450
580
             (Offering, '+index', OfferingView),
451
581
             (Offering, '+edit', OfferingEdit),