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

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

We do no longer need special interfaces for student access (IStudentPersonalEdit, IStudentClearanceEdit). Data access can be controlled by pages.

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