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

« back to all changes in this revision

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

  • Committer: William Grant
  • Date: 2010-02-12 02:54:34 UTC
  • Revision ID: grantw@unimelb.edu.au-20100212025434-c1bgt2yibmmpccx0
Add offering creation UI, and allow admins to change the subject or semester of existing offerings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
234
234
                    problems_done, problems_total))
235
235
 
236
236
 
 
237
class SubjectValidator(formencode.FancyValidator):
 
238
    """A FormEncode validator that turns a subject name into a subject.
 
239
 
 
240
    The state must have a 'store' attribute, which is the Storm store
 
241
    to use.
 
242
    """
 
243
    def _to_python(self, value, state):
 
244
        subject = state.store.find(Subject, short_name=value).one()
 
245
        if subject:
 
246
            return subject
 
247
        else:
 
248
            raise formencode.Invalid('Subject does not exist', value, state)
 
249
 
 
250
 
 
251
class SemesterValidator(formencode.FancyValidator):
 
252
    """A FormEncode validator that turns a string into a semester.
 
253
 
 
254
    The string should be of the form 'year/semester', eg. '2009/1'.
 
255
 
 
256
    The state must have a 'store' attribute, which is the Storm store
 
257
    to use.
 
258
    """
 
259
    def _to_python(self, value, state):
 
260
        try:
 
261
            year, semester = value.split('/')
 
262
        except ValueError:
 
263
            year = semester = None
 
264
 
 
265
        semester = state.store.find(
 
266
            Semester, year=year, semester=semester).one()
 
267
        if semester:
 
268
            return semester
 
269
        else:
 
270
            raise formencode.Invalid('Semester does not exist', value, state)
 
271
 
 
272
 
 
273
class OfferingUniquenessValidator(formencode.FancyValidator):
 
274
    """A FormEncode validator that checks that an offering is unique.
 
275
 
 
276
    There cannot be more than one offering in the same year and semester.
 
277
 
 
278
    The offering referenced by state.existing_offering is permitted to
 
279
    hold that year and semester tuple. If any other object holds it, the
 
280
    input is rejected.
 
281
    """
 
282
    def _to_python(self, value, state):
 
283
        if (state.store.find(
 
284
                Offering, subject=value['subject'],
 
285
                semester=value['semester']).one() not in
 
286
                (None, state.existing_offering)):
 
287
            raise formencode.Invalid(
 
288
                'Offering already exists', value, state)
 
289
        return value
 
290
 
 
291
 
237
292
class OfferingSchema(formencode.Schema):
238
293
    description = formencode.validators.UnicodeString(
239
294
        if_missing=None, not_empty=False)
240
295
    url = formencode.validators.URL(if_missing=None, not_empty=False)
241
296
 
242
297
 
243
 
class OfferingEdit(XHTMLView):
 
298
class OfferingAdminSchema(OfferingSchema):
 
299
    subject = formencode.All(
 
300
        SubjectValidator(), formencode.validators.UnicodeString())
 
301
    semester = formencode.All(
 
302
        SemesterValidator(), formencode.validators.UnicodeString())
 
303
    chained_validators = [OfferingUniquenessValidator()]
 
304
 
 
305
 
 
306
class BaseFormView(XHTMLView):
 
307
    """A base form view."""
 
308
 
 
309
    def filter(self, stream, ctx):
 
310
        return stream | HTMLFormFiller(data=ctx['data'])
 
311
 
 
312
    def populate_state(self, state):
 
313
        pass
 
314
 
 
315
    def get_return_url(self, obj):
 
316
        return self.req.publisher.generate(obj)
 
317
 
 
318
    def get_default_data(self, req):
 
319
        raise NotImplementedError()
 
320
 
 
321
    def save_object(self, req, data):
 
322
        raise NotImplementedError()
 
323
 
 
324
    @property
 
325
    def validator(self):
 
326
        raise NotImplementedError()
 
327
 
 
328
    def populate(self, req, ctx):
 
329
        if req.method == 'POST':
 
330
            data = dict(req.get_fieldstorage())
 
331
            try:
 
332
                self.populate_state(req)
 
333
                data = self.validator.to_python(data, state=req)
 
334
 
 
335
                obj = self.save_object(req, data)
 
336
 
 
337
                req.store.commit()
 
338
                req.throw_redirect(self.get_return_url(obj))
 
339
            except formencode.Invalid, e:
 
340
                error_value = e.msg
 
341
                errors = e.unpack_errors()
 
342
        else:
 
343
            data = self.get_default_data(req)
 
344
            error_value = None
 
345
            errors = {}
 
346
 
 
347
        if errors:
 
348
            req.store.rollback()
 
349
 
 
350
        ctx['req'] = req
 
351
        ctx['context'] = self.context
 
352
        ctx['data'] = data or {}
 
353
        ctx['errors'] = errors
 
354
        # If all of the fields validated, set the global form error.
 
355
        if isinstance(errors, basestring):
 
356
            ctx['error_value'] = errors
 
357
 
 
358
 
 
359
class OfferingEdit(BaseFormView):
244
360
    """A form to edit an offering's details."""
245
361
    template = 'templates/offering-edit.html'
246
362
    tab = 'subjects'
247
363
    permission = 'edit'
248
364
 
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()
 
365
    @property
 
366
    def validator(self):
 
367
        if self.req.user.admin:
 
368
            return OfferingAdminSchema()
265
369
        else:
266
 
            data = {
267
 
                'url': self.context.url,
268
 
                'description': self.context.description,
 
370
            return OfferingSchema()
 
371
 
 
372
    def populate(self, req, ctx):
 
373
        super(OfferingEdit, self).populate(req, ctx)
 
374
        ctx['subjects'] = req.store.find(Subject)
 
375
        ctx['semesters'] = req.store.find(Semester)
 
376
 
 
377
    def populate_state(self, state):
 
378
        state.existing_offering = self.context
 
379
 
 
380
    def get_default_data(self, req):
 
381
        return {
 
382
            'subject': self.context.subject.short_name,
 
383
            'semester': self.context.semester.year + '/' +
 
384
                        self.context.semester.semester,
 
385
            'url': self.context.url,
 
386
            'description': self.context.description,
269
387
            }
270
 
            errors = {}
271
 
 
272
 
        ctx['data'] = data or {}
273
 
        ctx['context'] = self.context
274
 
        ctx['errors'] = errors
 
388
 
 
389
    def save_object(self, req, data):
 
390
        if req.user.admin:
 
391
            self.context.subject = data['subject']
 
392
            self.context.semester = data['semester']
 
393
        self.context.description = data['description']
 
394
        self.context.url = unicode(data['url']) if data['url'] else None
 
395
        return self.context
 
396
 
 
397
 
 
398
class OfferingNew(BaseFormView):
 
399
    """A form to create an offering."""
 
400
    template = 'templates/offering-new.html'
 
401
    tab = 'subjects'
 
402
 
 
403
    def authorize(self, req):
 
404
        return req.user is not None and req.user.admin
 
405
 
 
406
    @property
 
407
    def validator(self):
 
408
        return OfferingAdminSchema()
 
409
 
 
410
    def populate(self, req, ctx):
 
411
        super(OfferingNew, self).populate(req, ctx)
 
412
        ctx['subjects'] = req.store.find(Subject)
 
413
        ctx['semesters'] = req.store.find(Semester)
 
414
 
 
415
    def populate_state(self, state):
 
416
        state.existing_offering = None
 
417
 
 
418
    def get_default_data(self, req):
 
419
        return {}
 
420
 
 
421
    def save_object(self, req, data):
 
422
        new_offering = Offering()
 
423
        new_offering.subject = data['subject']
 
424
        new_offering.semester = data['semester']
 
425
        new_offering.description = data['description']
 
426
        new_offering.url = unicode(data['url']) if data['url'] else None
 
427
 
 
428
        req.store.add(new_offering)
 
429
        return new_offering
275
430
 
276
431
 
277
432
class UserValidator(formencode.FancyValidator):
446
601
 
447
602
    views = [(ApplicationRoot, ('subjects', '+index'), SubjectsView),
448
603
             (ApplicationRoot, ('subjects', '+new'), SubjectNew),
 
604
             (ApplicationRoot, ('subjects', '+new-offering'), OfferingNew),
449
605
             (Subject, '+edit', SubjectEdit),
450
606
             (Offering, '+index', OfferingView),
451
607
             (Offering, '+edit', OfferingEdit),