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

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

Use getattr function instead of get method.

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