source: main/waeup.kofa/branches/henrik-transcript-workflow/src/waeup/kofa/university/certificate.py @ 16096

Last change on this file since 16096 was 14652, checked in by Henrik Bettermann, 8 years ago

Log also certificate code.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.3 KB
Line 
1## $Id: certificate.py 14652 2017-03-24 06:45:54Z 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, 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)
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        'waeup.local.CourseAdviser700',
52        'waeup.local.CourseAdviser800',
53        'waeup.local.DepartmentOfficer',
54        'waeup.local.ClearanceOfficer',
55        ]
56
57    def __init__(self, code=u'NA', title=u'Unnamed Certificate',
58                 study_mode=None, start_level=None,
59                 end_level=None, application_category=None,
60                 school_fee_1=None, school_fee_2=None,
61                 school_fee_3=None, school_fee_4=None,
62                 ratio=None, degree=None,
63                 custom_textline_1=None, custom_textline_2=None,
64                 custom_float_1=None, custom_float_2=None):
65        super(Certificate, self).__init__()
66        self.code = code
67        self.title = title
68        self.study_mode = study_mode
69        self.start_level = start_level
70        self.end_level = end_level
71        self.application_category = application_category
72        self.school_fee_1 = school_fee_1
73        self.school_fee_2 = school_fee_2
74        self.school_fee_3 = school_fee_3
75        self.school_fee_4 = school_fee_4
76        self.ratio = ratio
77        self.degree = degree
78        self.custom_textline_1 = custom_textline_1
79        self.custom_textline_2 = custom_textline_2
80        self.custom_float_1 = custom_float_1
81        self.custom_float_2 = custom_float_2
82
83    def traverse(self, name):
84        """Deliver appropriate containers.
85        """
86        if name == 'exports':
87            # create a virtual exports container and return it
88            container = VirtualCertificateExportJobContainer()
89            zope.location.location.located(container, self, 'exports')
90            return container
91        return None
92
93    @property
94    def longtitle(self):
95        return "%s (%s)" % (self.title,self.code)
96
97    def addCertCourse(self, course, level=100,
98                      mandatory=True, course_category=None):
99        """Add a certificate course.
100        """
101        code = "%s_%s" % (course.code, level)
102        self[code] = CertificateCourse(course, level, mandatory, course_category)
103        self[code].__parent__ = self
104        self[code].__name__ = code
105        self._p_changed = True
106
107    def delCertCourses(self, code, level=None):
108        """Delete certificate courses.
109
110        We might have more than one certificate course for a course.
111        If level is not provided all certificate courses referring
112        to the same course will be deleted.
113        """
114        keys = list(self.keys()) # create list copy
115        for key in keys:
116            if self[key].getCourseCode() != code:
117                continue
118            if level is not None and str(self[key].level) != str(level):
119                # found a course with correct key but wrong level...
120                continue
121            del self[key]
122            self._p_changed = True
123        return
124
125    def moveCertificate(self, facname, depname, newcode):
126        self.moved = True
127        cert = self
128        oldcode = cert.code
129        cert.code = newcode
130        del self.__parent__[oldcode]
131        newdep = grok.getSite()['faculties'][facname][depname]
132        newdep.certificates[newcode] = cert
133        #self.__parent__._p_changed = True
134        cat = getUtility(ICatalog, name='students_catalog')
135        results = cat.searchResults(certcode=(oldcode, oldcode))
136        for student in results:
137            notify(grok.ObjectModifiedEvent(student))
138            student.__parent__.logger.info(
139                '%s - Certificate moved' % student.__name__)
140        return
141
142class CertificateFactory(grok.GlobalUtility):
143    """A factory for certificates.
144    """
145    grok.implements(IFactory)
146    grok.name(u'waeup.Certificate')
147    title = u"Create a new certificate.",
148    description = u"This factory instantiates new certificate instances."
149
150    def __call__(self, *args, **kw):
151        return Certificate(*args, **kw)
152
153    def getInterfaces(self):
154        return implementedBy(Certificate)
155
156class CertificateCourse(grok.Model):
157    grok.implements(ICertificateCourse)
158
159    def __init__(self, course=None, level=100,
160                 mandatory=True, course_category=None):
161        self.course = course
162        self.level = level
163        self.mandatory = mandatory
164        self.course_category = course_category
165
166    def getCourseCode(self):
167        """Get code of a course.
168        """
169        return self.course.code
170
171    @property
172    def longtitle(self):
173        return "%s in level %s" % (self.course.code,
174                   course_levels.getTerm(self.level).title)
175
176class CertificateCourseFactory(grok.GlobalUtility):
177    """A factory for certificate courses.
178    """
179    grok.implements(IFactory)
180    grok.name(u'waeup.CertificateCourse')
181    title = u"Create a new certificate course.",
182    description = u"This factory instantiates new certificate courses."
183
184    def __call__(self, *args, **kw):
185        return CertificateCourse(*args, **kw)
186
187    def getInterfaces(self):
188        return implementedBy(CertificateCourse)
189
190@grok.subscribe(ICertificate, grok.IObjectRemovedEvent)
191def handle_certificate_removed(certificate, event):
192    """If a certificate is deleted, we make sure that also referrers to
193    student studycourse objects are removed.
194    """
195    # Do not remove referrer if certificate is going to move
196    if getattr(certificate, 'moved', False):
197        return
198
199    code = certificate.code
200
201    # Find all student studycourses that refer to given certificate...
202    try:
203        cat = getUtility(ICatalog, name='students_catalog')
204    except ComponentLookupError:
205        # catalog not available. This might happen during tests.
206        return
207
208    results = cat.searchResults(certcode=(code, code))
209    for student in results:
210        # Remove that referrer...
211        studycourse = student['studycourse']
212        studycourse.certificate = None
213        notify(grok.ObjectModifiedEvent(student))
214        student.__parent__.logger.info(
215            'ObjectRemovedEvent - %s - removed: certificate' % student.__name__)
216    return
217
218class CertificatesPlugin(grok.GlobalUtility):
219    """A plugin that updates certificates.
220    """
221
222    grok.implements(IKofaPluggable)
223    grok.name('certificates')
224
225    deprecated_attributes = []
226
227    def setup(self, site, name, logger):
228        return
229
230    def update(self, site, name, logger):
231        cat = getUtility(ICatalog, name='certificates_catalog')
232        results = cat.apply({'code':(None,None)})
233        uidutil = getUtility(IIntIds, context=cat)
234        items = getFields(ICertificate).items()
235        for r in results:
236            o = uidutil.getObject(r)
237            # Add new attributes
238            for i in items:
239                if not hasattr(o,i[0]):
240                    setattr(o,i[0],i[1].missing_value)
241                    logger.info(
242                        'CertificatesPlugin: %s attribute %s added.' % (
243                        o.code,i[0]))
244            # Remove deprecated attributes
245            for i in self.deprecated_attributes:
246                try:
247                    delattr(o,i)
248                    logger.info(
249                        'CertificatesPlugin: %s attribute %s deleted.' % (
250                        o.code,i))
251                except AttributeError:
252                    pass
253        return
254
255class CertificateCoursesPlugin(grok.GlobalUtility):
256    """A plugin that updates certificate courses.
257    """
258
259    grok.implements(IKofaPluggable)
260    grok.name('certcourses')
261
262    deprecated_attributes = []
263
264    def setup(self, site, name, logger):
265        return
266
267    def update(self, site, name, logger):
268        cat = getUtility(ICatalog, name='certcourses_catalog')
269        results = cat.apply({'course_code':(None,None)})
270        uidutil = getUtility(IIntIds, context=cat)
271        items = getFields(ICertificateCourse).items()
272        for r in results:
273            o = uidutil.getObject(r)
274            # Add new attributes
275            for i in items:
276                if not hasattr(o,i[0]):
277                    setattr(o,i[0],i[1].missing_value)
278                    logger.info(
279                        'CertificateCoursesPlugin: %s/%s_%s attribute %s added.' % (
280                        o.__parent__.code,o.course.code,o.level,i[0]))
281            # Remove deprecated attributes
282            for i in self.deprecated_attributes:
283                try:
284                    delattr(o,i)
285                    logger.info(
286                        'CertificateCoursesPlugin: %s/%s_%s attribute %s deleted.' % (
287                        o.__parent__.code,o.course.code,o.level,i))
288                except AttributeError:
289                    pass
290        return
Note: See TracBrowser for help on using the repository browser.