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

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

Set clearance_locked properly when importing students.

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