234
234
problems_done, problems_total))
237
class SubjectValidator(formencode.FancyValidator):
238
"""A FormEncode validator that turns a subject name into a subject.
240
The state must have a 'store' attribute, which is the Storm store
243
def _to_python(self, value, state):
244
subject = state.store.find(Subject, short_name=value).one()
248
raise formencode.Invalid('Subject does not exist', value, state)
251
class SemesterValidator(formencode.FancyValidator):
252
"""A FormEncode validator that turns a string into a semester.
254
The string should be of the form 'year/semester', eg. '2009/1'.
256
The state must have a 'store' attribute, which is the Storm store
259
def _to_python(self, value, state):
261
year, semester = value.split('/')
263
year = semester = None
265
semester = state.store.find(
266
Semester, year=year, semester=semester).one()
270
raise formencode.Invalid('Semester does not exist', value, state)
273
class OfferingUniquenessValidator(formencode.FancyValidator):
274
"""A FormEncode validator that checks that an offering is unique.
276
There cannot be more than one offering in the same year and semester.
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
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)
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)
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()]
306
class BaseFormView(XHTMLView):
307
"""A base form view."""
309
def filter(self, stream, ctx):
310
return stream | HTMLFormFiller(data=ctx['data'])
312
def populate_state(self, state):
315
def get_return_url(self, obj):
316
return self.req.publisher.generate(obj)
318
def get_default_data(self, req):
319
raise NotImplementedError()
321
def save_object(self, req, data):
322
raise NotImplementedError()
326
raise NotImplementedError()
328
def populate(self, req, ctx):
329
if req.method == 'POST':
330
data = dict(req.get_fieldstorage())
332
self.populate_state(req)
333
data = self.validator.to_python(data, state=req)
335
obj = self.save_object(req, data)
338
req.throw_redirect(self.get_return_url(obj))
339
except formencode.Invalid, e:
341
errors = e.unpack_errors()
343
data = self.get_default_data(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
359
class OfferingEdit(BaseFormView):
244
360
"""A form to edit an offering's details."""
245
361
template = 'templates/offering-edit.html'
247
363
permission = 'edit'
249
def filter(self, stream, ctx):
250
return stream | HTMLFormFiller(data=ctx['data'])
252
def populate(self, req, ctx):
253
if req.method == 'POST':
254
data = dict(req.get_fieldstorage())
256
validator = OfferingSchema()
257
data = validator.to_python(data, state=req)
259
self.context.url = unicode(data['url']) if data['url'] else None
260
self.context.description = data['description']
262
req.throw_redirect(req.publisher.generate(self.context))
263
except formencode.Invalid, e:
264
errors = e.unpack_errors()
367
if self.req.user.admin:
368
return OfferingAdminSchema()
267
'url': self.context.url,
268
'description': self.context.description,
370
return OfferingSchema()
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)
377
def populate_state(self, state):
378
state.existing_offering = self.context
380
def get_default_data(self, req):
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,
272
ctx['data'] = data or {}
273
ctx['context'] = self.context
274
ctx['errors'] = errors
389
def save_object(self, req, data):
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
398
class OfferingNew(BaseFormView):
399
"""A form to create an offering."""
400
template = 'templates/offering-new.html'
403
def authorize(self, req):
404
return req.user is not None and req.user.admin
408
return OfferingAdminSchema()
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)
415
def populate_state(self, state):
416
state.existing_offering = None
418
def get_default_data(self, req):
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
428
req.store.add(new_offering)
277
432
class UserValidator(formencode.FancyValidator):