source: main/waeup.kofa/trunk/src/waeup/kofa/students/student.py @ 8350

Last change on this file since 8350 was 8323, checked in by Henrik Bettermann, 12 years ago

We need to customize StudentStudyCourse? and StudentStudyLevel?. Therefore it's better to use the factory utility.

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