source: main/waeup.sirp/trunk/src/waeup/sirp/students/student.py @ 7641

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

Add current_mode index to students_catalog.

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