source: main/waeup.kofa/trunk/src/waeup/kofa/students/student.py @ 8384

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

Do not fire init transition when state has been imported.

  • Property svn:keywords set to Id
File size: 10.0 KB
RevLine 
[7191]1## $Id: student.py 8375 2012-05-07 05:16:39Z henrik $
2##
[6621]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"""
19Container for the various objects owned by students.
20"""
[7097]21import os
[6621]22import grok
[7949]23from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
[8323]24from zope.component import getUtility, createObject
[6749]25from zope.component.interfaces import IFactory
[6621]26from zope.interface import implementedBy
[6838]27from zope.securitypolicy.interfaces import IPrincipalRoleManager
[7949]28from waeup.kofa.image import KofaImageFile
29from waeup.kofa.imagestorage import DefaultFileStoreHandler
[7811]30from waeup.kofa.interfaces import (
[7359]31    IObjectHistory, IUserAccount, IFileStoreNameChooser, IFileStoreHandler,
[7819]32    IKofaUtils, CLEARANCE, registration_states_vocab)
[7949]33from waeup.kofa.students.accommodation import StudentAccommodation
[7811]34from waeup.kofa.students.interfaces import IStudent, IStudentNavigation
[7949]35from waeup.kofa.students.payments import StudentPaymentsContainer
[7811]36from waeup.kofa.students.studycourse import StudentStudyCourse
[7949]37from waeup.kofa.students.utils import generate_student_id
[7811]38from waeup.kofa.utils.helpers import attrs_to_fields
[6621]39
[7949]40
[6621]41class Student(grok.Container):
42    """This is a student container for the various objects
43    owned by students.
44    """
[7538]45    grok.implements(IStudent, IStudentNavigation)
[6621]46    grok.provides(IStudent)
47
48    def __init__(self):
49        super(Student, self).__init__()
[6749]50        # The site doesn't exist in unit tests
[6652]51        try:
[6749]52            students = grok.getSite()['students']
53            self.student_id = generate_student_id(students,'?')
54        except TypeError:
[6666]55            self.student_id = u'Z654321'
[6699]56        self.password = None
[6621]57        return
58
[6637]59    def loggerInfo(self, ob_class, comment=None):
60        target = self.__name__
61        return grok.getSite()['students'].logger_info(ob_class,target,comment)
62
63    @property
[7364]64    def display_fullname(self):
[7357]65        middlename = getattr(self, 'middlename', None)
[7819]66        kofa_utils = getUtility(IKofaUtils)
[7811]67        return kofa_utils.fullname(self.firstname, self.lastname, middlename)
[7357]68
69    @property
[7364]70    def fullname(self):
71        middlename = getattr(self, 'middlename', None)
72        if middlename:
73            return '%s-%s-%s' % (self.firstname.lower(),
74                middlename.lower(), self.lastname.lower())
75        else:
76            return '%s-%s' % (self.firstname.lower(), self.lastname.lower())
77
78    @property
[6637]79    def state(self):
80        state = IWorkflowState(self).getState()
81        return state
82
83    @property
[7677]84    def translated_state(self):
85        state = registration_states_vocab.getTermByToken(
86            self.state).title
87        return state
88
89    @property
[6637]90    def history(self):
91        history = IObjectHistory(self)
92        return history
93
[6642]94    def getStudent(self):
95        return self
96
[6814]97    @property
[7203]98    def certcode(self):
[6814]99        cert = getattr(self.get('studycourse', None), 'certificate', None)
[7203]100        if cert is not None:
101            return cert.code
102        return
[6814]103
[7062]104    @property
[7203]105    def faccode(self):
106        cert = getattr(self.get('studycourse', None), 'certificate', None)
107        if cert is not None:
108            return cert.__parent__.__parent__.__parent__.code
109        return
110
111    @property
112    def depcode(self):
113        cert = getattr(self.get('studycourse', None), 'certificate', None)
114        if cert is not None:
115            return cert.__parent__.__parent__.code
116        return
117
118    @property
[7062]119    def current_session(self):
[7948]120        session = getattr(
121            self.get('studycourse', None), 'current_session', None)
[7641]122        return session
[7062]123
[7641]124    @property
125    def current_mode(self):
[7948]126        certificate = getattr(
127            self.get('studycourse', None), 'certificate', None)
[7641]128        if certificate is not None:
129            return certificate.study_mode
130        return
131
[6621]132# Set all attributes of Student required in IStudent as field
133# properties. Doing this, we do not have to set initial attributes
134# ourselves and as a bonus we get free validation when an attribute is
135# set.
136Student = attrs_to_fields(Student)
137
138class StudentFactory(grok.GlobalUtility):
139    """A factory for students.
140    """
141    grok.implements(IFactory)
142    grok.name(u'waeup.Student')
143    title = u"Create a new student.",
144    description = u"This factory instantiates new student instances."
145
146    def __call__(self, *args, **kw):
147        return Student()
148
149    def getInterfaces(self):
150        return implementedBy(Student)
[6836]151
[6838]152@grok.subscribe(IStudent, grok.IObjectAddedEvent)
[6839]153def handle_student_added(student, event):
[6838]154    """If a student is added all subcontainers are automatically added
[7948]155    and the transition create is fired. The latter produces a logging
156    message.
[6838]157    """
[8375]158    if student.state == CLEARANCE:
[7527]159        student.clearance_locked = False
160    else:
161        student.clearance_locked = True
[8323]162    studycourse = createObject(u'waeup.StudentStudyCourse')
[6838]163    student['studycourse'] = studycourse
[6859]164    payments = StudentPaymentsContainer()
[6838]165    student['payments'] = payments
166    accommodation = StudentAccommodation()
167    student['accommodation'] = accommodation
168    # Assign global student role for new student
169    account = IUserAccount(student)
170    account.roles = ['waeup.Student']
171    # Assign local StudentRecordOwner role
172    role_manager = IPrincipalRoleManager(student)
173    role_manager.assignRoleToPrincipal(
174        'waeup.local.StudentRecordOwner', student.student_id)
[8375]175    if student.state is None:
[7513]176        IWorkflowInfo(student).fireTransition('create')
[6838]177    return
178
[6836]179@grok.subscribe(IStudent, grok.IObjectRemovedEvent)
[6839]180def handle_student_removed(student, event):
[6836]181    """If a student is removed a message is logged.
182    """
183    comment = 'Student record removed'
184    target = student.student_id
[6841]185    try:
[7652]186        grok.getSite()['students'].logger.info('%s - %s' % (
187            target, comment))
[7212]188    except KeyError:
189        # If we delete an entire university instance there won't be
190        # a students subcontainer
191        return
[7097]192    return
193
194#: The file id marker for student files
195STUDENT_FILE_STORE_NAME = 'file-student'
196
197class StudentFileNameChooser(grok.Adapter):
[7099]198    """A file id chooser for :class:`Student` objects.
[7097]199
[7099]200    `context` is an :class:`Student` instance.
[7097]201
[7099]202    The :class:`StudentImageNameChooser` can build/check file ids for
203    :class:`Student` objects suitable for use with
[7097]204    :class:`ExtFileStore` instances. The delivered file_id contains
[7099]205    the file id marker for :class:`Student` object and the student id
206    of the context student.
[7097]207
208    This chooser is registered as an adapter providing
[7811]209    :class:`waeup.kofa.interfaces.IFileStoreNameChooser`.
[7097]210
211    File store name choosers like this one are only convenience
[7099]212    components to ease the task of creating file ids for student
[7097]213    objects. You are nevertheless encouraged to use them instead of
[7099]214    manually setting up filenames for students.
[7097]215
[7811]216    .. seealso:: :mod:`waeup.kofa.imagestorage`
[7097]217
218    """
219    grok.context(IStudent)
220    grok.implements(IFileStoreNameChooser)
221
222    def checkName(self, name=None, attr=None):
223        """Check whether the given name is a valid file id for the context.
224
225        Returns ``True`` only if `name` equals the result of
226        :meth:`chooseName`.
227
228        """
229        return name == self.chooseName()
230
[7106]231    def chooseName(self, attr, name=None):
[7097]232        """Get a valid file id for student context.
233
234        *Example:*
235
[7105]236        For a student with student id ``'A123456'`` and
[7106]237        with attr ``'nice_image.jpeg'`` stored in
[7097]238        the students container this chooser would create:
239
[7106]240          ``'__file-student__students/A/A123456/nice_image_A123456.jpeg'``
[7097]241
242        meaning that the nice image of this applicant would be
243        stored in the site-wide file storage in path:
244
[7106]245          ``students/A/A123456/nice_image_A123456.jpeg``
[7097]246
247        """
[7106]248        basename, ext = os.path.splitext(attr)
[7099]249        stud_id = self.context.student_id
[7106]250        marked_filename = '__%s__%s/%s/%s_%s%s' % (
[7948]251            STUDENT_FILE_STORE_NAME, stud_id[0], stud_id, basename,
252            stud_id, ext)
[7097]253        return marked_filename
254
255
256class StudentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
257    """Student specific file handling.
258
259    This handler knows in which path in a filestore to store student
260    files and how to turn this kind of data into some (browsable)
261    file object.
262
263    It is called from the global file storage, when it wants to
264    get/store a file with a file id starting with
265    ``__file-student__`` (the marker string for student files).
266
267    Like each other file store handler it does not handle the files
268    really (this is done by the global file store) but only computes
269    paths and things like this.
270    """
271    grok.implements(IFileStoreHandler)
272    grok.name(STUDENT_FILE_STORE_NAME)
273
274    def pathFromFileID(self, store, root, file_id):
[7099]275        """All student files are put in directory ``students``.
[7097]276        """
277        marker, filename, basename, ext = store.extractMarker(file_id)
[7122]278        sub_root = os.path.join(root, 'students')
279        return super(StudentFileStoreHandler, self).pathFromFileID(
280            store, sub_root, basename)
[7097]281
282    def createFile(self, store, root, filename, file_id, file):
283        """Create a browsable file-like object.
284        """
[7122]285        # call super method to ensure that any old files with
286        # different filename extension are deleted.
287        file, path, file_obj =  super(
288            StudentFileStoreHandler, self).createFile(
289            store, root,  filename, file_id, file)
[7819]290        return file, path, KofaImageFile(
[7122]291            file_obj.filename, file_obj.data)
Note: See TracBrowser for help on using the repository browser.