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

Last change on this file since 7357 was 7357, checked in by Henrik Bettermann, 14 years ago

Replace fullname form field by first-, middle- and lastname form fields.

We don't need a studentaddpage.pt.

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