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

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

Replace the term 'WAeUP' by SIRP which is a WAeUP product.

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