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

Last change on this file since 7948 was 7948, checked in by uli, 13 years ago

Shorten overlong lines.

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