source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/applicant.py @ 7339

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

Start implementation of applicant copier.

Reorganize interfaces in the applicants package. We need a base interface which can be used in the students package too, for storing the application data. The upcoming StudentApplication? class has to implement the IApplicantBaseData interface. So, data fields have to be isolated.

  • Property svn:keywords set to Id
File size: 9.6 KB
Line 
1## $Id: applicant.py 7338 2011-12-13 17:26:35Z 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##
18import os
19import grok
20from grok import index
21from zope.component.interfaces import IFactory
22from zope.component import createObject
23from zope.securitypolicy.interfaces import IPrincipalRoleManager
24from zope.interface import implementedBy
25from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
26from waeup.sirp.app import University
27from waeup.sirp.image import SIRPImageFile
28from waeup.sirp.imagestorage import DefaultFileStoreHandler
29from waeup.sirp.interfaces import (
30    IObjectHistory, IFileStoreHandler, IFileStoreNameChooser)
31from waeup.sirp.utils.helpers import attrs_to_fields, get_current_principal
32from waeup.sirp.applicants.interfaces import (
33    IApplicant, IApplicantEdit,
34    )
35
36class Applicant(grok.Container):
37    grok.implements(IApplicant,IApplicantEdit)
38    grok.provides(IApplicant)
39
40    def __init__(self):
41        super(Applicant, self).__init__()
42        self.password = None
43        IWorkflowInfo(self).fireTransition('init')
44        self.application_date = None
45        self.applicant_id = None
46        return
47
48    def loggerInfo(self, ob_class, comment=None):
49        target = self.applicant_id
50        return grok.getSite()['applicants'].logger_info(ob_class,target,comment)
51
52    @property
53    def state(self):
54        state = IWorkflowState(self).getState()
55        return state
56
57    @property
58    def history(self):
59        history = IObjectHistory(self)
60        return history
61
62    @property
63    def application_number(self):
64        try:
65            return self.applicant_id.split('_')[1]
66        except AttributeError:
67            return None
68
69    @property
70    def fullname(self):
71        # We do not necessarily have the middlenames attribute
72        middlenames = getattr(self, 'middlenames', None)
73        if middlenames:
74            return '%s %s %s' % (self.firstname,
75                middlenames, self.lastname)
76        else:
77            return '%s %s' % (self.firstname, self.lastname)
78
79    def createStudent(self):
80        """Create a student object from applicatnt data
81        and copy applicant object.
82        """
83        student = createObject(u'waeup.Student')
84        site = grok.getSite()
85
86        site['students'].addStudent(student)
87        student.fullname = self.fullname
88        return student.student_id
89
90# Set all attributes of Applicant required in IApplicant as field
91# properties. Doing this, we do not have to set initial attributes
92# ourselves and as a bonus we get free validation when an attribute is
93# set.
94Applicant = attrs_to_fields(Applicant)
95
96class ApplicantCatalog(grok.Indexes):
97    """A catalog indexing :class:`Applicant` instances in the ZODB.
98    """
99    grok.site(University)
100    grok.name('applicants_catalog')
101    grok.context(IApplicant)
102
103    access_code = index.Field(attribute='access_code')
104    applicant_id = index.Field(attribute='applicant_id')
105    reg_number = index.Field(attribute='reg_number')
106
107class ApplicantFactory(grok.GlobalUtility):
108    """A factory for applicants.
109    """
110    grok.implements(IFactory)
111    grok.name(u'waeup.Applicant')
112    title = u"Create a new applicant.",
113    description = u"This factory instantiates new applicant instances."
114
115    def __call__(self, *args, **kw):
116        return Applicant()
117
118    def getInterfaces(self):
119        return implementedBy(Applicant)
120
121
122#: The file id marker for applicant passport images
123APPLICANT_IMAGE_STORE_NAME = 'img-applicant'
124
125class ApplicantImageNameChooser(grok.Adapter):
126    """A file id chooser for :class:`Applicant` objects.
127
128    `context` is an :class:`Applicant` instance.
129
130    The :class:`ApplicantImageNameChooser` can build/check file ids
131    for :class:`Applicant` objects suitable for use with
132    :class:`ExtFileStore` instances. The delivered file_id contains
133    the file id marker for :class:`Applicant` object and the
134    registration number or access code of the context applicant. Also
135    the name of the connected applicant container will be part of the
136    generated file id.
137
138    This chooser is registered as an adapter providing
139    :class:`waeup.sirp.interfaces.IFileStoreNameChooser`.
140
141    File store name choosers like this one are only convenience
142    components to ease the task of creating file ids for applicant
143    objects. You are nevertheless encouraged to use them instead of
144    manually setting up filenames for applicants.
145
146    .. seealso:: :mod:`waeup.sirp.imagestorage`
147
148    """
149    grok.context(IApplicant)
150    grok.implements(IFileStoreNameChooser)
151
152    def checkName(self, name=None, attr=None):
153        """Check whether the given name is a valid file id for the context.
154
155        Returns ``True`` only if `name` equals the result of
156        :meth:`chooseName`.
157
158        The `attr` parameter is not taken into account for
159        :class:`Applicant` context as the single passport image is the
160        only file we store for applicants.
161        """
162        return name == self.chooseName()
163
164    def chooseName(self, name=None, attr=None):
165        """Get a valid file id for applicant context.
166
167        *Example:*
168
169        For an applicant with applicant_id. ``'app2001_1234'``
170        and stored in an applicants container called
171        ``'mycontainer'``, this chooser would create:
172
173          ``'__img-applicant__mycontainer/app2001_1234.jpg'``
174
175        meaning that the passport image of this applicant would be
176        stored in the site-wide file storage in path:
177
178          ``mycontainer/app2001_1234.jpg``
179
180        If the context applicant has no parent, ``'_default'`` is used
181        as parent name.
182
183        The `attr` parameter is not taken into account for
184        :class:`Applicant` context as the single passport image is the
185        only file we store for applicants.
186
187        """
188        parent_name = getattr(
189            getattr(self.context, '__parent__', None),
190            '__name__', '_default')
191        marked_filename = '__%s__%s/%s.jpg' % (
192            APPLICANT_IMAGE_STORE_NAME,
193            parent_name, self.context.applicant_id)
194        return marked_filename
195
196
197class ApplicantImageStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
198    """Applicant specific image handling.
199
200    This handler knows in which path in a filestore to store applicant
201    images and how to turn this kind of data into some (browsable)
202    file object.
203
204    It is called from the global file storage, when it wants to
205    get/store a file with a file id starting with
206    ``__img-applicant__`` (the marker string for applicant images).
207
208    Like each other file store handler it does not handle the files
209    really (this is done by the global file store) but only computes
210    paths and things like this.
211    """
212    grok.implements(IFileStoreHandler)
213    grok.name(APPLICANT_IMAGE_STORE_NAME)
214
215    def pathFromFileID(self, store, root, file_id):
216        """All applicants images are filed in directory ``applicants``.
217        """
218        marker, filename, basename, ext = store.extractMarker(file_id)
219        sub_root = os.path.join(root, 'applicants')
220        return super(ApplicantImageStoreHandler, self).pathFromFileID(
221            store, sub_root, basename)
222
223    def createFile(self, store, root, filename, file_id, file):
224        """Create a browsable file-like object.
225        """
226        ext = os.path.splitext(filename)[1].lower()
227        if ext not in ['.jpg', '.png']:
228            raise ValueError('Only .jpg and .png allowed')
229        # call super method to ensure that any old files with
230        # different filename extension are deleted.
231        file, path, file_obj =  super(
232            ApplicantImageStoreHandler, self).createFile(
233            store, root,  filename, file_id, file)
234        return file, path, SIRPImageFile(
235            file_obj.filename, file_obj.data)
236
237@grok.subscribe(IApplicant, grok.IObjectAddedEvent)
238def handle_applicant_added(applicant, event):
239    """If an applicant is added local and site roles are assigned.
240    """
241    role_manager = IPrincipalRoleManager(applicant)
242    role_manager.assignRoleToPrincipal(
243        'waeup.local.ApplicationOwner', applicant.applicant_id)
244    # Assign current principal the global Applicant role
245    role_manager = IPrincipalRoleManager(grok.getSite())
246    role_manager.assignRoleToPrincipal(
247        'waeup.Applicant', applicant.applicant_id)
248
249    # Assign global applicant role for new applicant (alternative way)
250    #account = IUserAccount(applicant)
251    #account.roles = ['waeup.Applicant']
252
253    return
254
255@grok.subscribe(IApplicant, grok.IObjectRemovedEvent)
256def handle_applicant_removed(applicant, event):
257    """If an applicant is removed a message is logged.
258    """
259    comment = 'Applicant record removed'
260    target = applicant.applicant_id
261    # In some tests we don't have a principal
262    try:
263        user = get_current_principal().id
264    except (TypeError, AttributeError):
265        return
266    try:
267        grok.getSite()['applicants'].logger.info('%s - %s - %s' % (
268            user, target, comment))
269    except KeyError:
270        # If we delete an entire university instance there won't be
271        # an applicants subcontainer
272        return
273    return
Note: See TracBrowser for help on using the repository browser.