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

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

Implement fullname constructor as a global utility method which can easily be subject to customization. I think it's better to leave this here rather than selecting the constructor method via the customization object.

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