source: main/waeup.sirp/trunk/src/waeup/sirp/students/vocabularies.py @ 10222

Last change on this file since 10222 was 7691, checked in by Henrik Bettermann, 13 years ago

Translate StudyLevelSource?. Unfortunately, unittests don't know about i18n.

  • Property svn:keywords set to Id
File size: 6.9 KB
Line 
1## $Id: vocabularies.py 7691 2012-02-23 15:53:39Z 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.sirp.interfaces import SimpleSIRPVocabulary
28from waeup.sirp.interfaces import MessageFactory as _
29from waeup.sirp.students.lgas import LGAS
30from waeup.sirp.students.nats import NATS
31from waeup.sirp.university.vocabularies import course_levels
32
33lgas_vocab = SimpleSIRPVocabulary(
34    *sorted([(x[1],x[0]) for x in LGAS]))
35
36nats_vocab = SimpleSIRPVocabulary(
37    *sorted([(x[1],x[0]) for x in NATS]))
38
39def study_levels(studycourse):
40    if studycourse.certificate is not None:
41        start_level = int(studycourse.certificate.start_level)
42        end_level = int(studycourse.certificate.end_level)
43        if start_level == 10:
44            levels = [10,] + [level for level in range(100,end_level+200,10)
45                if level % 100 < 30]
46        else:
47            levels = [level for level in range(start_level,end_level+200,10)
48                if level % 100 < 30]
49    else:
50        # default level range
51        levels = [level for level in range(100,800,10) if level % 100 < 30]
52    return levels
53
54
55class StudyLevelSource(BasicContextualSourceFactory):
56    """The StudyLevelSource is based on and extends the
57    course_levels vocabulary defined in the university package.
58    Repeating study levels are denoted by increments of 10, the
59    first spillover level by the certificate's end level plus 100
60    and the second spillover level by the end level plus 110.
61    """
62    def getValues(self, context):
63        return study_levels(context)
64
65    def getToken(self, context, value):
66        return str(value)
67
68    def getTitle(self, context, value):
69        if context.certificate is not None:
70            start_level = int(context.certificate.start_level)
71            end_level = int(context.certificate.end_level)
72        else:
73            # default level range
74            start_level = 100
75            end_level = 600
76        if value < start_level or value > end_level + 120:
77            return _('Error: level id ${value} out of range',
78                mapping={'value': value})
79        # Special treatment for pre-studies level
80        if value == 10:
81            return course_levels.by_value[value].title
82        level,repeat = divmod(value, 100)
83        level = level * 100
84        repeat = repeat//10
85        title = course_levels.by_value[level].title
86        if level > end_level and repeat == 1:
87            title = course_levels.by_value[level - 100].title
88            return _('${title} 2nd spillover', mapping={'title': title})
89        if level > end_level and repeat == 2:
90            title = course_levels.by_value[level - 100].title
91            return _('${title} 3rd spillover', mapping={'title': title})
92        if level > end_level:
93            title = course_levels.by_value[level - 100].title
94            return  _('${title} 1st spillover', mapping={'title': title})
95        if repeat == 1:
96            return _('${title} on 1st probation', mapping={'title': title})
97        if repeat == 2:
98            return _('${title} on 2nd probation', mapping={'title': title})
99        return title
100
101class CertificateSource(BasicContextualSourceFactory):
102    """A certificate source delivers all certificates provided
103    in the portal.
104    """
105    def getValues(self, context):
106        catalog = getUtility(ICatalog, name='certificates_catalog')
107        return sorted(list(
108                catalog.searchResults(
109                    code=('', 'z*'))),
110                    key=lambda value: value.code)
111
112    def getToken(self, context, value):
113        return value.code
114
115    def getTitle(self, context, value):
116        return "%s - %s" % (value.code, value.title[:64])
117
118
119class GenderSource(BasicSourceFactory):
120    """A gender source delivers basically a mapping
121       ``{'m': 'Male', 'f': 'Female'}``
122
123       Using a source, we make sure that the tokens (which are
124       stored/expected for instance from CSV files) are something one
125       can expect and not cryptic IntIDs.
126    """
127    def getValues(self):
128        return ['m', 'f']
129
130    def getToken(self, value):
131        return value[0].lower()
132
133    def getTitle(self, value):
134        if value == 'm':
135            return _('Male')
136        if value == 'f':
137            return _('Female')
138
139class RegNumNotInSource(ValidationError):
140    """Registration number exists already
141    """
142    # The docstring of ValidationErrors is used as error description
143    # by zope.formlib.
144    pass
145
146class MatNumNotInSource(ValidationError):
147    """Matriculation number exists already
148    """
149    # The docstring of ValidationErrors is used as error description
150    # by zope.formlib.
151    pass
152
153class RegNumberSource(object):
154    implements(ISource)
155    cat_name = 'students_catalog'
156    field_name = 'reg_number'
157    validation_error = RegNumNotInSource
158    def __init__(self, context):
159        self.context = context
160        return
161
162    def __contains__(self, value):
163        cat = queryUtility(ICatalog, self.cat_name)
164        if cat is None:
165            return True
166        kw = {self.field_name: (value, value)}
167        results = cat.searchResults(**kw)
168        for entry in results:
169            if entry.student_id != self.context.student_id:
170                # XXX: sources should simply return False.
171                #      But then we get some stupid error message in forms
172                #      when validation fails.
173                raise self.validation_error(value)
174                #return False
175        return True
176
177def contextual_reg_num_source(context):
178    source = RegNumberSource(context)
179    return source
180directlyProvides(contextual_reg_num_source, IContextSourceBinder)
181
182class MatNumberSource(RegNumberSource):
183    field_name = 'matric_number'
184    validation_error = MatNumNotInSource
185
186def contextual_mat_num_source(context):
187    source = MatNumberSource(context)
188    return source
189directlyProvides(contextual_mat_num_source, IContextSourceBinder)
Note: See TracBrowser for help on using the repository browser.