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

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

Translate student registration worklow titles, messages and destination states.

Show translated states on pages.

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