source: main/waeup.kofa/trunk/src/waeup/kofa/students/vocabularies.py @ 8963

Last change on this file since 8963 was 8940, checked in by Henrik Bettermann, 12 years ago

Add 999 to study_levels.

Check conversion of level only if in row.

  • Property svn:keywords set to Id
File size: 7.6 KB
Line 
1## $Id: vocabularies.py 8940 2012-07-08 06:01:16Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""Vocabularies and sources for the student section.
19"""
20from zope.component import getUtility, queryUtility
21from zope.catalog.interfaces import ICatalog
22from zope.interface import implements, directlyProvides
23from zope.schema.interfaces import ISource, IContextSourceBinder
24from zope.schema.interfaces import ValidationError
25from zc.sourcefactory.basic import BasicSourceFactory
26from zc.sourcefactory.contextual import BasicContextualSourceFactory
27from waeup.kofa.interfaces import SimpleKofaVocabulary
28from waeup.kofa.interfaces import MessageFactory as _
29from waeup.kofa.utils.helpers import get_sorted_preferred
30from waeup.kofa.utils.countries import COUNTRIES
31from waeup.kofa.university.vocabularies import course_levels
32
33
34#: a tuple of tuples (<COUNTRY-NAME>, <ISO-CODE>) with Nigeria first.
35COUNTRIES = get_sorted_preferred(COUNTRIES, ['NG'])
36nats_vocab = SimpleKofaVocabulary(*COUNTRIES)
37
38def study_levels(studycourse):
39    if studycourse.certificate is not None:
40        start_level = int(studycourse.certificate.start_level)
41        end_level = int(studycourse.certificate.end_level)
42        if start_level == 999 or end_level == 999:
43            levels = [999]
44        elif start_level == 10:
45            levels = [10,] + [level for level in range(100,end_level+200,10)
46                if level % 100 < 30]
47        else:
48            levels = [level for level in range(start_level,end_level+200,10)
49                if level % 100 < 30]
50    else:
51        # default level range
52        levels = [level for level in range(100,1000,10) if level % 100 < 30]
53        levels.append(999)
54    return levels
55
56
57class StudyLevelSource(BasicContextualSourceFactory):
58    """The StudyLevelSource is based on and extends the
59    course_levels vocabulary defined in the university package.
60    Repeating study levels are denoted by increments of 10, the
61    first spillover level by the certificate's end level plus 100
62    and the second spillover level by the end level plus 110.
63    """
64    def getValues(self, context):
65        return study_levels(context)
66
67    def getToken(self, context, value):
68        return str(value)
69
70    def getTitle(self, context, value):
71        if context.certificate is not None:
72            start_level = int(context.certificate.start_level)
73            end_level = int(context.certificate.end_level)
74        else:
75            # default level range
76            start_level = 100
77            end_level = 1000
78        if start_level == 999 or end_level == 999:
79            if value != 999:
80                return _('Error: wrong level id ${value}',
81                    mapping={'value': value})
82            return course_levels.by_value[999].title
83        if value < start_level or value > end_level + 120:
84            return _('Error: level id ${value} out of range',
85                mapping={'value': value})
86        # Special treatment for pre-studies level
87        if value == 10:
88            return course_levels.by_value[value].title
89        level,repeat = divmod(value, 100)
90        level = level * 100
91        repeat = repeat//10
92        title = course_levels.by_value[level].title
93        if level > end_level and repeat == 1:
94            title = course_levels.by_value[level - 100].title
95            return _('${title} 2nd spillover', mapping={'title': title})
96        if level > end_level and repeat == 2:
97            title = course_levels.by_value[level - 100].title
98            return _('${title} 3rd spillover', mapping={'title': title})
99        if level > end_level:
100            title = course_levels.by_value[level - 100].title
101            return  _('${title} 1st spillover', mapping={'title': title})
102        if repeat == 1:
103            return _('${title} on 1st probation', mapping={'title': title})
104        if repeat == 2:
105            return _('${title} on 2nd probation', mapping={'title': title})
106        return title
107
108class GenderSource(BasicSourceFactory):
109    """A gender source delivers basically a mapping
110       ``{'m': 'Male', 'f': 'Female'}``
111
112       Using a source, we make sure that the tokens (which are
113       stored/expected for instance from CSV files) are something one
114       can expect and not cryptic IntIDs.
115    """
116    def getValues(self):
117        return ['m', 'f']
118
119    def getToken(self, value):
120        return value[0].lower()
121
122    def getTitle(self, value):
123        if value == 'm':
124            return _('male')
125        if value == 'f':
126            return _('female')
127
128class RegNumNotInSource(ValidationError):
129    """Registration number exists already
130    """
131    # The docstring of ValidationErrors is used as error description
132    # by zope.formlib.
133    pass
134
135class MatNumNotInSource(ValidationError):
136    """Matriculation number exists already
137    """
138    # The docstring of ValidationErrors is used as error description
139    # by zope.formlib.
140    pass
141
142class RegNumberSource(object):
143    """A source that accepts any entry for a certain field if not used
144    already.
145
146    Using this kind of source means a way of setting an invariant.
147
148    We accept a value iff:
149    - the value cannot be found in catalog or
150    - the value can be found as part of some item but the bound item
151      is the context object itself.
152    """
153    implements(ISource)
154    cat_name = 'students_catalog'
155    field_name = 'reg_number'
156    validation_error = RegNumNotInSource
157    comp_field = 'student_id'
158    def __init__(self, context):
159        self.context = context
160        return
161
162    def __contains__(self, value):
163        """We accept all values not already given to other students.
164        """
165        cat = queryUtility(ICatalog, self.cat_name)
166        if cat is None:
167            return True
168        kw = {self.field_name: (value, value)}
169        results = cat.searchResults(**kw)
170        for entry in results:
171            if not hasattr(self.context, self.comp_field):
172                # we have no context with comp_field (most probably
173                # while adding a new object, where the container is
174                # the context) which means that the value was given
175                # already to another object (as _something_ was found in
176                # the catalog with that value). Fail on first round.
177                raise self.validation_error(value)
178            if getattr(entry, self.comp_field) != getattr(
179                self.context, self.comp_field):
180                # An entry already given to another student is not in our
181                # range of acceptable values.
182                raise self.validation_error(value)
183                #return False
184        return True
185
186def contextual_reg_num_source(context):
187    source = RegNumberSource(context)
188    return source
189directlyProvides(contextual_reg_num_source, IContextSourceBinder)
190
191class MatNumberSource(RegNumberSource):
192    field_name = 'matric_number'
193    validation_error = MatNumNotInSource
194
195def contextual_mat_num_source(context):
196    source = MatNumberSource(context)
197    return source
198directlyProvides(contextual_mat_num_source, IContextSourceBinder)
Note: See TracBrowser for help on using the repository browser.