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

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

We need to customize StudentStudyCourse? and StudentStudyLevel?. Therefore it's better to use the factory utility.

  • Property svn:keywords set to Id
File size: 10.0 KB
RevLine 
[7191]1## $Id: student.py 8323 2012-05-02 08:17:14Z 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    """
[8287]158    state = IWorkflowState(student).getState()
159    if state == CLEARANCE:
[7527]160        student.clearance_locked = False
161    else:
162        student.clearance_locked = True
[8323]163    studycourse = createObject(u'waeup.StudentStudyCourse')
[6838]164    student['studycourse'] = studycourse
[6859]165    payments = StudentPaymentsContainer()
[6838]166    student['payments'] = payments
167    accommodation = StudentAccommodation()
168    student['accommodation'] = accommodation
169    # Assign global student role for new student
170    account = IUserAccount(student)
171    account.roles = ['waeup.Student']
172    # Assign local StudentRecordOwner role
173    role_manager = IPrincipalRoleManager(student)
174    role_manager.assignRoleToPrincipal(
175        'waeup.local.StudentRecordOwner', student.student_id)
[8287]176    if state is None:
[7513]177        IWorkflowInfo(student).fireTransition('create')
[6838]178    return
179
[6836]180@grok.subscribe(IStudent, grok.IObjectRemovedEvent)
[6839]181def handle_student_removed(student, event):
[6836]182    """If a student is removed a message is logged.
183    """
184    comment = 'Student record removed'
185    target = student.student_id
[6841]186    try:
[7652]187        grok.getSite()['students'].logger.info('%s - %s' % (
188            target, comment))
[7212]189    except KeyError:
190        # If we delete an entire university instance there won't be
191        # a students subcontainer
192        return
[7097]193    return
194
195#: The file id marker for student files
196STUDENT_FILE_STORE_NAME = 'file-student'
197
198class StudentFileNameChooser(grok.Adapter):
[7099]199    """A file id chooser for :class:`Student` objects.
[7097]200
[7099]201    `context` is an :class:`Student` instance.
[7097]202
[7099]203    The :class:`StudentImageNameChooser` can build/check file ids for
204    :class:`Student` objects suitable for use with
[7097]205    :class:`ExtFileStore` instances. The delivered file_id contains
[7099]206    the file id marker for :class:`Student` object and the student id
207    of the context student.
[7097]208
209    This chooser is registered as an adapter providing
[7811]210    :class:`waeup.kofa.interfaces.IFileStoreNameChooser`.
[7097]211
212    File store name choosers like this one are only convenience
[7099]213    components to ease the task of creating file ids for student
[7097]214    objects. You are nevertheless encouraged to use them instead of
[7099]215    manually setting up filenames for students.
[7097]216
[7811]217    .. seealso:: :mod:`waeup.kofa.imagestorage`
[7097]218
219    """
220    grok.context(IStudent)
221    grok.implements(IFileStoreNameChooser)
222
223    def checkName(self, name=None, attr=None):
224        """Check whether the given name is a valid file id for the context.
225
226        Returns ``True`` only if `name` equals the result of
227        :meth:`chooseName`.
228
229        """
230        return name == self.chooseName()
231
[7106]232    def chooseName(self, attr, name=None):
[7097]233        """Get a valid file id for student context.
234
235        *Example:*
236
[7105]237        For a student with student id ``'A123456'`` and
[7106]238        with attr ``'nice_image.jpeg'`` stored in
[7097]239        the students container this chooser would create:
240
[7106]241          ``'__file-student__students/A/A123456/nice_image_A123456.jpeg'``
[7097]242
243        meaning that the nice image of this applicant would be
244        stored in the site-wide file storage in path:
245
[7106]246          ``students/A/A123456/nice_image_A123456.jpeg``
[7097]247
248        """
[7106]249        basename, ext = os.path.splitext(attr)
[7099]250        stud_id = self.context.student_id
[7106]251        marked_filename = '__%s__%s/%s/%s_%s%s' % (
[7948]252            STUDENT_FILE_STORE_NAME, stud_id[0], stud_id, basename,
253            stud_id, ext)
[7097]254        return marked_filename
255
256
257class StudentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
258    """Student specific file handling.
259
260    This handler knows in which path in a filestore to store student
261    files and how to turn this kind of data into some (browsable)
262    file object.
263
264    It is called from the global file storage, when it wants to
265    get/store a file with a file id starting with
266    ``__file-student__`` (the marker string for student files).
267
268    Like each other file store handler it does not handle the files
269    really (this is done by the global file store) but only computes
270    paths and things like this.
271    """
272    grok.implements(IFileStoreHandler)
273    grok.name(STUDENT_FILE_STORE_NAME)
274
275    def pathFromFileID(self, store, root, file_id):
[7099]276        """All student files are put in directory ``students``.
[7097]277        """
278        marker, filename, basename, ext = store.extractMarker(file_id)
[7122]279        sub_root = os.path.join(root, 'students')
280        return super(StudentFileStoreHandler, self).pathFromFileID(
281            store, sub_root, basename)
[7097]282
283    def createFile(self, store, root, filename, file_id, file):
284        """Create a browsable file-like object.
285        """
[7122]286        # call super method to ensure that any old files with
287        # different filename extension are deleted.
288        file, path, file_obj =  super(
289            StudentFileStoreHandler, self).createFile(
290            store, root,  filename, file_id, file)
[7819]291        return file, path, KofaImageFile(
[7122]292            file_obj.filename, file_obj.data)
Note: See TracBrowser for help on using the repository browser.