source: main/waeup.kofa/trunk/src/waeup/kofa/university/certificate.py @ 9963

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

The catalog tests now reflect that the the students_catalog is _not_ updated when the certificate's study_mode has changed. Automatic students_catalog updates would significantly slow down certificate imports.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 8.1 KB
Line 
1## $Id: certificate.py 9940 2013-02-11 14:36:22Z 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"""Kofa certificates and certificate courses
19"""
20import grok
21import zope.location.location
22from zope.event import notify
23from zope.catalog.interfaces import ICatalog
24from zope.intid.interfaces import IIntIds
25from zope.schema import getFields
26from zope.component import getUtility
27from zope.component.interfaces import IFactory, ComponentLookupError
28from zope.interface import implementedBy
29from waeup.kofa.interfaces import IKofaPluggable
30from waeup.kofa.university.interfaces import (
31    ICertificate, ICertificateAdd, ICertificateCourse)
32from waeup.kofa.university.vocabularies import course_levels
33from waeup.kofa.utils.batching import VirtualExportJobContainer
34
35class VirtualCertificateExportJobContainer(VirtualExportJobContainer):
36    """A virtual export job container for certificates.
37    """
38
39class Certificate(grok.Container):
40    """A certificate.
41    """
42    grok.implements(ICertificate, ICertificateAdd)
43
44    local_roles = [
45        'waeup.local.CourseAdviser100',
46        'waeup.local.CourseAdviser200',
47        'waeup.local.CourseAdviser300',
48        'waeup.local.CourseAdviser400',
49        'waeup.local.CourseAdviser500',
50        'waeup.local.CourseAdviser600',
51        ]
52
53    def __init__(self, code=u'NA', title=u'Unnamed Certificate',
54                 study_mode=None, start_level=None,
55                 end_level=None, application_category=None,
56                 school_fee_1=None, school_fee_2=None,
57                 school_fee_3=None, school_fee_4=None):
58        super(Certificate, self).__init__()
59        self.code = code
60        self.title = title
61        self.study_mode = study_mode
62        self.start_level = start_level
63        self.end_level = end_level
64        self.application_category = application_category
65        self.school_fee_1 = school_fee_1
66        self.school_fee_2 = school_fee_2
67        self.school_fee_3 = school_fee_3
68        self.school_fee_4 = school_fee_4
69
70    def traverse(self, name):
71        """Deliver appropriate containers.
72        """
73        if name == 'exports':
74            # create a virtual exports container and return it
75            container = VirtualCertificateExportJobContainer()
76            zope.location.location.located(container, self, 'exports')
77            return container
78        return None
79
80    def longtitle(self):
81        return "%s (%s)" % (self.title,self.code)
82
83    def addCertCourse(self, course, level=100, mandatory=True):
84        """Add a certificate course.
85        """
86        code = "%s_%s" % (course.code, level)
87        self[code] = CertificateCourse(course, level, mandatory)
88        self[code].__parent__ = self
89        self[code].__name__ = code
90        self._p_changed = True
91
92    def delCertCourses(self, code, level=None):
93        """Delete certificate courses.
94
95        We might have more than one certificate course for a course.
96        If level is not provided all certificate courses referring
97        to the same course will be deleted.
98        """
99        keys = list(self.keys()) # create list copy
100        for key in keys:
101            if self[key].getCourseCode() != code:
102                continue
103            if level is not None and str(self[key].level) != str(level):
104                # found a course with correct key but wrong level...
105                continue
106            del self[key]
107            self._p_changed = True
108        return
109
110    def moveCertificate(self, fac, dep):
111        self.moved = True
112        cert = self
113        del self.__parent__[cert.code]
114        grok.getSite()['faculties'][fac][dep].certificates[cert.code] = cert
115        self.__parent__._p_changed = True
116        cat = getUtility(ICatalog, name='students_catalog')
117        results = cat.searchResults(certcode=(cert.code, cert.code))
118        for student in results:
119            notify(grok.ObjectModifiedEvent(student))
120            student.__parent__.logger.info(
121                '%s - Certificate moved' % student.__name__)
122
123        return
124
125class CertificateFactory(grok.GlobalUtility):
126    """A factory for certificates.
127    """
128    grok.implements(IFactory)
129    grok.name(u'waeup.Certificate')
130    title = u"Create a new certificate.",
131    description = u"This factory instantiates new certificate instances."
132
133    def __call__(self, *args, **kw):
134        return Certificate(*args, **kw)
135
136    def getInterfaces(self):
137        return implementedBy(Certificate)
138
139class CertificateCourse(grok.Model):
140    grok.implements(ICertificateCourse)
141
142    def __init__(self, course=None, level=100, mandatory=True):
143        self.course = course
144        self.level = level
145        self.mandatory = mandatory
146
147    def getCourseCode(self):
148        """Get code of a course.
149        """
150        return self.course.code
151
152    def longtitle(self):
153        return "%s in level %s" % (self.course.code,
154                   course_levels.getTerm(self.level).title)
155
156class CertificateCourseFactory(grok.GlobalUtility):
157    """A factory for certificate courses.
158    """
159    grok.implements(IFactory)
160    grok.name(u'waeup.CertificateCourse')
161    title = u"Create a new certificate course.",
162    description = u"This factory instantiates new certificate courses."
163
164    def __call__(self, *args, **kw):
165        return CertificateCourse(*args, **kw)
166
167    def getInterfaces(self):
168        return implementedBy(CertificateCourse)
169
170@grok.subscribe(ICertificate, grok.IObjectRemovedEvent)
171def handle_certificate_removed(certificate, event):
172    """If a certificate is deleted, we make sure that also referrers to
173    student studycourse objects are removed.
174    """
175    # Do not remove referrer if certificate is going to move
176    if getattr(certificate, 'moved', False):
177        return
178
179    code = certificate.code
180
181    # Find all student studycourses that refer to given certificate...
182    try:
183        cat = getUtility(ICatalog, name='students_catalog')
184    except ComponentLookupError:
185        # catalog not available. This might happen during tests.
186        return
187
188    results = cat.searchResults(certcode=(code, code))
189    for student in results:
190        # Remove that referrer...
191        studycourse = student['studycourse']
192        studycourse.certificate = None
193        notify(grok.ObjectModifiedEvent(student))
194        student.__parent__.logger.info(
195            'ObjectRemovedEvent - %s - removed: certificate' % student.__name__)
196    return
197
198class CertificatesPlugin(grok.GlobalUtility):
199    """A plugin that updates certificates.
200    """
201
202    grok.implements(IKofaPluggable)
203    grok.name('certificates')
204
205    deprecated_attributes = []
206
207    def setup(self, site, name, logger):
208        return
209
210    def update(self, site, name, logger):
211        cat = getUtility(ICatalog, name='certificates_catalog')
212        results = cat.apply({'code':(None,None)})
213        uidutil = getUtility(IIntIds, context=cat)
214        items = getFields(ICertificate).items()
215        for r in results:
216            o = uidutil.getObject(r)
217            # Add new attributes
218            for i in items:
219                if not hasattr(o,i[0]):
220                    setattr(o,i[0],i[1].missing_value)
221                    logger.info(
222                        'CertificatesPlugin: %s attribute %s added.' % (
223                        o.code,i[0]))
224            # Remove deprecated attributes
225            for i in self.deprecated_attributes:
226                try:
227                    delattr(o,i)
228                    logger.info(
229                        'CertificatesPlugin: %s attribute %s deleted.' % (
230                        o.code,i))
231                except AttributeError:
232                    pass
233        return
Note: See TracBrowser for help on using the repository browser.