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

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

Let applicants cooperate with new imagestorage. Fix tests.

Extend permissions for PortalManager?.

  • Property svn:keywords set to Id
File size: 8.6 KB
Line 
1## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
2## This program is free software; you can redistribute it and/or modify
3## it under the terms of the GNU General Public License as published by
4## the Free Software Foundation; either version 2 of the License, or
5## (at your option) any later version.
6##
7## This program is distributed in the hope that it will be useful,
8## but WITHOUT ANY WARRANTY; without even the implied warranty of
9## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10## GNU General Public License for more details.
11##
12## You should have received a copy of the GNU General Public License
13## along with this program; if not, write to the Free Software
14## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15##
16"""
17Container for the various objects owned by students.
18"""
19import os
20import grok
21from grok import index
22from zope.component.interfaces import IFactory
23from zope.interface import implementedBy
24from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
25from zope.securitypolicy.interfaces import IPrincipalRoleManager
26from waeup.sirp.interfaces import (
27    IObjectHistory, IUserAccount, IFileStoreNameChooser, IFileStoreHandler)
28from waeup.sirp.image import WAeUPImageFile
29from waeup.sirp.imagestorage import DefaultFileStoreHandler
30from waeup.sirp.students.interfaces import (
31    IStudent, IStudentNavigation, IStudentClearanceEdit,
32    IStudentPersonalEdit)
33from waeup.sirp.students.studycourse import StudentStudyCourse
34from waeup.sirp.students.payments import StudentPaymentsContainer
35from waeup.sirp.students.accommodation import StudentAccommodation
36from waeup.sirp.utils.helpers import attrs_to_fields, get_current_principal
37from waeup.sirp.students.utils import generate_student_id
38
39class Student(grok.Container):
40    """This is a student container for the various objects
41    owned by students.
42    """
43    grok.implements(IStudent, IStudentNavigation,
44        IStudentPersonalEdit, IStudentClearanceEdit)
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 state(self):
64        state = IWorkflowState(self).getState()
65        return state
66
67    @property
68    def history(self):
69        history = IObjectHistory(self)
70        return history
71
72    def getStudent(self):
73        return self
74
75    @property
76    def certificate(self):
77        cert = getattr(self.get('studycourse', None), 'certificate', None)
78        return cert
79
80    @property
81    def current_session(self):
82        cert = getattr(self.get('studycourse', None), 'current_session', None)
83        return cert
84
85# Set all attributes of Student required in IStudent as field
86# properties. Doing this, we do not have to set initial attributes
87# ourselves and as a bonus we get free validation when an attribute is
88# set.
89Student = attrs_to_fields(Student)
90
91class StudentFactory(grok.GlobalUtility):
92    """A factory for students.
93    """
94    grok.implements(IFactory)
95    grok.name(u'waeup.Student')
96    title = u"Create a new student.",
97    description = u"This factory instantiates new student instances."
98
99    def __call__(self, *args, **kw):
100        return Student()
101
102    def getInterfaces(self):
103        return implementedBy(Student)
104
105@grok.subscribe(IStudent, grok.IObjectAddedEvent)
106def handle_student_added(student, event):
107    """If a student is added all subcontainers are automatically added
108    and the transition create is fired. The latter produces a logging message.
109    """
110    student.clearance_locked = True
111    studycourse = StudentStudyCourse()
112    student['studycourse'] = studycourse
113    payments = StudentPaymentsContainer()
114    student['payments'] = payments
115    accommodation = StudentAccommodation()
116    student['accommodation'] = accommodation
117    # Assign global student role for new student
118    account = IUserAccount(student)
119    account.roles = ['waeup.Student']
120    # Assign local StudentRecordOwner role
121    role_manager = IPrincipalRoleManager(student)
122    role_manager.assignRoleToPrincipal(
123        'waeup.local.StudentRecordOwner', student.student_id)
124    IWorkflowInfo(student).fireTransition('create')
125    return
126
127@grok.subscribe(IStudent, grok.IObjectRemovedEvent)
128def handle_student_removed(student, event):
129    """If a student is removed a message is logged.
130    """
131    comment = 'Student record removed'
132    target = student.student_id
133    # In some tests we don't have a principal
134    try:
135        user = get_current_principal().id
136    except (TypeError, AttributeError):
137        return
138    grok.getSite()['students'].logger.info('%s - %s - %s' % (
139        user, target, comment))
140    return
141
142#: The file id marker for student files
143STUDENT_FILE_STORE_NAME = 'file-student'
144
145class StudentFileNameChooser(grok.Adapter):
146    """A file id chooser for :class:`Student` objects.
147
148    `context` is an :class:`Student` instance.
149
150    The :class:`StudentImageNameChooser` can build/check file ids for
151    :class:`Student` objects suitable for use with
152    :class:`ExtFileStore` instances. The delivered file_id contains
153    the file id marker for :class:`Student` object and the student id
154    of the context student.
155
156    This chooser is registered as an adapter providing
157    :class:`waeup.sirp.interfaces.IFileStoreNameChooser`.
158
159    File store name choosers like this one are only convenience
160    components to ease the task of creating file ids for student
161    objects. You are nevertheless encouraged to use them instead of
162    manually setting up filenames for students.
163
164    .. seealso:: :mod:`waeup.sirp.imagestorage`
165
166    """
167    grok.context(IStudent)
168    grok.implements(IFileStoreNameChooser)
169
170    def checkName(self, name=None, attr=None):
171        """Check whether the given name is a valid file id for the context.
172
173        Returns ``True`` only if `name` equals the result of
174        :meth:`chooseName`.
175
176        """
177        return name == self.chooseName()
178
179    def chooseName(self, attr, name=None):
180        """Get a valid file id for student context.
181
182        *Example:*
183
184        For a student with student id ``'A123456'`` and
185        with attr ``'nice_image.jpeg'`` stored in
186        the students container this chooser would create:
187
188          ``'__file-student__students/A/A123456/nice_image_A123456.jpeg'``
189
190        meaning that the nice image of this applicant would be
191        stored in the site-wide file storage in path:
192
193          ``students/A/A123456/nice_image_A123456.jpeg``
194
195        """
196        basename, ext = os.path.splitext(attr)
197        stud_id = self.context.student_id
198        marked_filename = '__%s__%s/%s/%s_%s%s' % (
199            STUDENT_FILE_STORE_NAME, stud_id[0], stud_id, basename, stud_id, ext)
200        return marked_filename
201
202
203class StudentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
204    """Student specific file handling.
205
206    This handler knows in which path in a filestore to store student
207    files and how to turn this kind of data into some (browsable)
208    file object.
209
210    It is called from the global file storage, when it wants to
211    get/store a file with a file id starting with
212    ``__file-student__`` (the marker string for student files).
213
214    Like each other file store handler it does not handle the files
215    really (this is done by the global file store) but only computes
216    paths and things like this.
217    """
218    grok.implements(IFileStoreHandler)
219    grok.name(STUDENT_FILE_STORE_NAME)
220
221    def pathFromFileID(self, store, root, file_id):
222        """All student files are put in directory ``students``.
223        """
224        marker, filename, basename, ext = store.extractMarker(file_id)
225        sub_root = os.path.join(root, 'students')
226        return super(StudentFileStoreHandler, self).pathFromFileID(
227            store, sub_root, basename)
228
229    def createFile(self, store, root, filename, file_id, file):
230        """Create a browsable file-like object.
231        """
232        ext = os.path.splitext(filename)[1].lower()
233        if ext not in ['.jpg', '.png']:
234            raise ValueError('Only .jpg and .png allowed')
235        # call super method to ensure that any old files with
236        # different filename extension are deleted.
237        file, path, file_obj =  super(
238            StudentFileStoreHandler, self).createFile(
239            store, root,  filename, file_id, file)
240        return file, path, WAeUPImageFile(
241            file_obj.filename, file_obj.data)
Note: See TracBrowser for help on using the repository browser.