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

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

It's quite tedious to setup the correct StudyLevelSource?. Now we have 2 probation levels and 3 spill-over levels. Pre-studies level (=10) can't be repeated.

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