## $Id: vocabularies.py 7625 2012-02-10 20:05:55Z henrik $ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Vocabularies and sources for the student section. """ from zope.component import getUtility, queryUtility from zope.catalog.interfaces import ICatalog from zope.interface import implements, directlyProvides from zope.schema.interfaces import ISource, IContextSourceBinder from zope.schema.interfaces import ValidationError from zc.sourcefactory.basic import BasicSourceFactory from zc.sourcefactory.contextual import BasicContextualSourceFactory from waeup.sirp.interfaces import SimpleSIRPVocabulary from waeup.sirp.students.lgas import LGAS from waeup.sirp.students.nats import NATS from waeup.sirp.university.vocabularies import course_levels lgas_vocab = SimpleSIRPVocabulary( *sorted([(x[1],x[0]) for x in LGAS])) nats_vocab = SimpleSIRPVocabulary( *sorted([(x[1],x[0]) for x in NATS])) def study_levels(studycourse): if studycourse.certificate is not None: start_level = int(studycourse.certificate.start_level) end_level = int(studycourse.certificate.end_level) if start_level == 10: levels = [10,] + [level for level in range(100,end_level+200,10) if level % 100 < 30] else: levels = [level for level in range(start_level,end_level+200,10) if level % 100 < 30] else: # default level range levels = [level for level in range(100,800,10) if level % 100 < 30] return levels class StudyLevelSource(BasicContextualSourceFactory): """The StudyLevelSource is based on and extends the course_levels vocabulary defined in the university package. Repeating study levels are denoted by increments of 10, the first spillover level by the certificate's end level plus 100 and the second spillover level by the end level plus 110. """ def getValues(self, context): return study_levels(context) def getToken(self, context, value): return str(value) def getTitle(self, context, value): if context.certificate is not None: start_level = int(context.certificate.start_level) end_level = int(context.certificate.end_level) else: # default level range start_level = 100 end_level = 600 if value < start_level or value > end_level + 120: return "Error: level id %s out of range" % value # Special treatment for pre-studies level if value == 10: return course_levels.by_value[value].title level,repeat = divmod(value, 100) level = level * 100 repeat = repeat//10 title = course_levels.by_value[level].title if level > end_level and repeat == 1: title = course_levels.by_value[level - 100].title return "%s 2nd spillover" % title if level > end_level and repeat == 2: title = course_levels.by_value[level - 100].title return "%s 3rd spillover" % title if level > end_level: title = course_levels.by_value[level - 100].title return "%s 1st spillover" % title if repeat == 1: return "%s on 1st probation" % title if repeat == 2: return "%s on 2nd probation" % title return title class CertificateSource(BasicContextualSourceFactory): """A certificate source delivers all certificates provided in the portal. """ def getValues(self, context): catalog = getUtility(ICatalog, name='certificates_catalog') return sorted(list( catalog.searchResults( code=('', 'z*'))), key=lambda value: value.code) def getToken(self, context, value): return value.code def getTitle(self, context, value): return "%s - %s" % (value.code, value.title[:64]) class GenderSource(BasicSourceFactory): """A gender source delivers basically a mapping ``{'m': 'Male', 'f': 'Female'}`` Using a source, we make sure that the tokens (which are stored/expected for instance from CSV files) are something one can expect and not cryptic IntIDs. """ def getValues(self): return ['m', 'f'] def getToken(self, value): return value[0].lower() def getTitle(self, value): if value == 'm': return 'Male' if value == 'f': return 'Female' class RegNumNotInSource(ValidationError): """Registration number exists already """ # The docstring of ValidationErrors is used as error description # by zope.formlib. pass class MatNumNotInSource(ValidationError): """Matriculation number exists already """ # The docstring of ValidationErrors is used as error description # by zope.formlib. pass class RegNumberSource(object): implements(ISource) cat_name = 'students_catalog' field_name = 'reg_number' validation_error = RegNumNotInSource def __init__(self, context): self.context = context return def __contains__(self, value): cat = queryUtility(ICatalog, self.cat_name) if cat is None: return True kw = {self.field_name: (value, value)} results = cat.searchResults(**kw) for entry in results: if entry.student_id != self.context.student_id: # XXX: sources should simply return False. # But then we get some stupid error message in forms # when validation fails. raise self.validation_error(value) #return False return True def contextual_reg_num_source(context): source = RegNumberSource(context) return source directlyProvides(contextual_reg_num_source, IContextSourceBinder) class MatNumberSource(RegNumberSource): field_name = 'matric_number' validation_error = MatNumNotInSource def contextual_mat_num_source(context): source = MatNumberSource(context) return source directlyProvides(contextual_mat_num_source, IContextSourceBinder)