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

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

Catch exception in handle_student_removed when entire university instances are being removed.

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