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

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