source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py @ 7229

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

Adjust copyright statement and svn keyword in applicants.

  • Property svn:keywords set to Id
File size: 15.6 KB
Line 
1## $Id: interfaces.py 7192 2011-11-25 07:15:50Z 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##
18"""Interfaces of the university application package.
19"""
20import os
21import re
22
23from grokcore.content.interfaces import IContainer
24
25from zope import schema
26from zope.interface import Interface, Attribute
27from zope.component import getUtilitiesFor
28from zope.pluggableauth.interfaces import IPrincipalInfo
29from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
30from zc.sourcefactory.basic import BasicSourceFactory
31from waeup.sirp.interfaces import IWAeUPObject, year_range
32from waeup.sirp.university.vocabularies import application_categories
33from waeup.sirp.students.vocabularies import (
34  lgas_vocab, CertificateSource, GenderSource,
35  )
36from waeup.sirp.applicants.vocabularies import (
37  application_types_vocab, application_pins_vocab,
38  AppCatCertificateSource,
39  )
40
41#: Maximum upload size for applicant passport photographs (in bytes)
42MAX_UPLOAD_SIZE = 1024 * 20
43
44# Define a valiation method for email addresses
45class NotAnEmailAddress(schema.ValidationError):
46    __doc__ = u"Invalid email address"
47
48check_email = re.compile(
49    r"[a-zA-Z0-9._%-]+@([a-zA-Z0-9-]+.)*[a-zA-Z]{2,4}").match
50def validate_email(value):
51    if not check_email(value):
52        raise NotAnEmailAddress(value)
53    return True
54
55class ApplicantContainerProviderSource(BasicSourceFactory):
56    """A source offering all available applicants container types.
57
58    The values returned by this source are names of utilities that can
59    create :class:`ApplicantContainer` instances. So, if you get a
60    name like ``'myactype'`` from this source, then you can do:
61
62      >>> from zope.component import getUtility
63      >>> p = getUtility(IApplicantsContainerProvider, name=myactype)
64      >>> my_applicants_container = p.factory()
65
66    Or you can access class-attributes like
67
68      >>> my_applicants_container.container_title
69      'Pretty'
70
71    """
72    def getValues(self):
73        """Returns a list of ``(<name>, <provider>)`` tuples.
74
75        Here ``<name>`` is the name under which an
76        :class:``ApplicantContainerProvider`` was registered as a
77        utility and ``<provider>`` is the utility itself.
78        """
79        return getUtilitiesFor(IApplicantsContainerProvider)
80
81    def getToken(self, value):
82        """Return the name of the ``(<name>, <provider>)`` tuple.
83        """
84        return value[0]
85
86    def getTitle(self, value):
87        """Get a 'title - description' string for a container type.
88        """
89        factory = value[1].factory
90        return "%s - %s" % (
91            factory.container_title, factory.container_description)
92
93class IResultEntry(IWAeUPObject):
94    subject = schema.TextLine(
95        title = u'Subject',
96        description = u'The subject',
97        required=False,
98        )
99    score = schema.TextLine(
100        title = u'Score',
101        description = u'The score',
102        required=False,
103        )
104
105class IApplicantsRoot(IWAeUPObject, IContainer):
106    """A container for university applicants containers.
107    """
108    pass
109
110class IApplicantsContainer(IWAeUPObject):
111    """An applicants container contains university applicants.
112
113    """
114
115    container_title = Attribute(
116        u'classattribute: title for type of container')
117    container_description = Attribute(
118        u'classattribute: description for type of container')
119
120
121    code = schema.TextLine(
122        title = u'Code',
123        default = u'-',
124        required = True,
125        readonly = True,
126        )
127
128    title = schema.TextLine(
129        title = u'Title',
130        required = True,
131        default = u'-',
132        readonly = True,
133        )
134
135    prefix = schema.Choice(
136        title = u'Application target',
137        required = True,
138        default = None,
139        source = application_types_vocab,
140        readonly = True,
141        )
142
143    year = schema.Choice(
144        title = u'Year of entrance',
145        required = True,
146        default = None,
147        values = year_range(),
148        readonly = True,
149        )
150
151    provider = schema.Choice(
152        title = u'Applicants container type',
153        required = True,
154        default = None,
155        source = ApplicantContainerProviderSource(),
156        readonly = True,
157        )
158
159    ac_prefix = schema.Choice(
160        title = u'Access code prefix',
161        required = True,
162        default = None,
163        source = application_pins_vocab,
164        )
165
166    application_category = schema.Choice(
167        title = u'Category for the grouping of certificates',
168        required = True,
169        default = None,
170        source = application_categories,
171        )
172
173    description = schema.Text(
174        title = u'Human readable description in reST format',
175        required = False,
176        default = u'''This text can been seen by anonymous users.
177Here we put information about the study courses provided, the application procedure and deadlines.'''
178        )
179
180    startdate = schema.Date(
181        title = u'Application start date',
182        required = False,
183        default = None,
184        )
185
186    enddate = schema.Date(
187        title = u'Application closing date',
188        required = False,
189        default = None,
190        )
191
192    strict_deadline = schema.Bool(
193        title = u'Forbid additions after deadline (enddate)',
194        required = True,
195        default = True,
196        )
197
198    def archive(id=None):
199        """Create on-dist archive of applicants stored in this term.
200
201        If id is `None`, all applicants are archived.
202
203        If id contains a single id string, only the respective
204        applicants are archived.
205
206        If id contains a list of id strings all of the respective
207        applicants types are saved to disk.
208        """
209
210    def clear(id=None, archive=True):
211        """Remove applicants of type given by 'id'.
212
213        Optionally archive the applicants.
214
215        If id is `None`, all applicants are archived.
216
217        If id contains a single id string, only the respective
218        applicants are archived.
219
220        If id contains a list of id strings all of the respective
221        applicant types are saved to disk.
222
223        If `archive` is ``False`` none of the archive-handling is done
224        and respective applicants are simply removed from the
225        database.
226        """
227
228class IApplicantsContainerAdd(IApplicantsContainer):
229    """An applicants container contains university applicants.
230    """
231    prefix = schema.Choice(
232        title = u'Application target',
233        required = True,
234        default = None,
235        source = application_types_vocab,
236        readonly = False,
237        )
238
239    year = schema.Choice(
240        title = u'Year of entrance',
241        required = True,
242        default = None,
243        values = year_range(),
244        readonly = False,
245        )
246
247    provider = schema.Choice(
248        title = u'Applicants container type',
249        required = True,
250        default = None,
251        source = ApplicantContainerProviderSource(),
252        readonly = False,
253        )
254
255IApplicantsContainerAdd[
256    'prefix'].order =  IApplicantsContainer['prefix'].order
257IApplicantsContainerAdd[
258    'year'].order =  IApplicantsContainer['year'].order
259IApplicantsContainerAdd[
260    'provider'].order =  IApplicantsContainer['provider'].order
261
262class IApplicantBaseData(IWAeUPObject):
263    """The data for an applicant.
264
265    This is a base interface with no field (except ``reg_no``)
266    required. For use with importers, forms, etc., please use one of
267    the derived interfaces below, which set more fields to required
268    state, depending on use-case.
269    """
270    history = Attribute('Object history, a list of messages.')
271    state = Attribute('Returns the application state of an applicant')
272    application_date = Attribute('Date of submission, used for export only')
273
274    #def getApplicantsRootLogger():
275    #    """Returns the logger from the applicants root object
276    #    """
277
278    def loggerInfo(ob_class, comment):
279        """Adds an INFO message to the log file
280        """
281
282    reg_no = schema.TextLine(
283        title = u'JAMB Registration Number',
284        readonly = True,
285        )
286    access_code = schema.TextLine(
287        title = u'Access Code',
288        required = False,
289        readonly = True,
290        )
291    course1 = schema.Choice(
292        title = u'1st Choice Course of Study',
293        source = AppCatCertificateSource(),
294        required = True,
295        )
296    course2 = schema.Choice(
297        title = u'2nd Choice Course of Study',
298        source = AppCatCertificateSource(),
299        required = False,
300        )
301    firstname = schema.TextLine(
302        title = u'First Name',
303        required = True,
304        )
305    middlenames = schema.TextLine(
306        title = u'Middle Names',
307        required = False,
308        )
309    lastname = schema.TextLine(
310        title = u'Last Name (Surname)',
311        required = True,
312        )
313    date_of_birth = schema.Date(
314        title = u'Date of Birth',
315        required = True,
316        )
317    lga = schema.Choice(
318        source = lgas_vocab,
319        title = u'State/LGA',
320        default = 'foreigner',
321        required = True,
322        )
323    sex = schema.Choice(
324        title = u'Sex',
325        source = GenderSource(),
326        default = u'm',
327        required = True,
328        )
329    email = schema.ASCIILine(
330        title = u'Email',
331        required = False,
332        constraint=validate_email,
333        )
334    phone = schema.Int(
335        title = u'Phone',
336        description = u'Enter phone number with country code and without spaces.',
337        required = False,
338        )
339    #passport = ImageFile(
340    #    title = u'Passport Photograph',
341    #    #default = DEFAULT_PASSPORT_IMAGE_MALE,
342    #    defaultFactory = default_passport_image,
343    #    description = u'Maximun file size is 20 kB.',
344    #    required = True,
345    #    max_size = 20480,
346    #    )
347
348    #
349    # Process Data
350    #
351    screening_score = schema.Int(
352        title = u'Screening Score',
353        required = False,
354        )
355    screening_venue = schema.TextLine(
356        title = u'Screening Venue',
357        required = False,
358        )
359    course_admitted = schema.Choice(
360        title = u'Admitted Course of Study',
361        source = CertificateSource(),
362        default = None,
363        required = False,
364        )
365    notice = schema.Text(
366        title = u'Notice',
367        required = False,
368        )
369    student_id = schema.TextLine(
370        title = u'Student ID',
371        required = False,
372        readonly = True,
373        )
374    locked = schema.Bool(
375        title = u'Form locked',
376        default = False,
377        )
378
379class IApplicant(IApplicantBaseData):
380    """An applicant.
381
382    This is basically the applicant base data. Here we repeat the
383    fields from base data if we have to set the `required` attribute
384    to True (which is the default).
385    """
386
387class IApplicantEdit(IApplicantBaseData):
388    """An applicant.
389
390    Here we can repeat the fields from base data and set the
391    `required` and `readonly` attributes to True to further restrict
392    the data access. We cannot omit fields. This has to be done in the
393    respective form page.
394    """
395    screening_score = schema.Int(
396        title = u'Screening Score',
397        required = False,
398        readonly = True,
399        )
400    screening_venue = schema.TextLine(
401        title = u'Screening Venue',
402        required = False,
403        readonly = True,
404        )
405    course_admitted = schema.Choice(
406        title = u'Admitted Course of Study',
407        source = CertificateSource(),
408        default = None,
409        required = False,
410        readonly = True,
411        )
412    notice = schema.Text(
413        title = u'Notice',
414        required = False,
415        readonly = True,
416        )
417
418class IApplicantPrincipalInfo(IPrincipalInfo):
419    """Infos about principals that are applicants.
420    """
421    access_code = Attribute("The Access Code the user purchased")
422
423class IApplicantPrincipal(IPrincipal):
424    """A principal that is an applicant.
425
426    This interface extends zope.security.interfaces.IPrincipal and
427    requires also an `id` and other attributes defined there.
428    """
429    access_code = schema.TextLine(
430        title = u'Access Code',
431        description = u'The access code purchased by the user.',
432        required = True,
433        readonly = True)
434
435class IApplicantsFormChallenger(Interface):
436    """A challenger that uses a browser form to collect applicant
437       credentials.
438    """
439    loginpagename = schema.TextLine(
440        title = u'Loginpagename',
441        description = u"""Name of the login form used by challenger.
442
443        The form must provide an ``access_code`` input field.
444        """)
445
446    accesscode_field = schema.TextLine(
447        title = u'Access code field',
448        description = u'''Field of the login page which is looked up for
449                          access_code''',
450        default = u'access_code',
451        )
452
453
454class IApplicantSessionCredentials(Interface):
455    """Interface for storing and accessing applicant credentials in a
456       session.
457    """
458
459    def __init__(access_code):
460        """Create applicant session credentials."""
461
462    def getAccessCode():
463        """Return the access code."""
464
465
466class IApplicantsContainerProvider(Interface):
467    """A provider for applicants containers.
468
469    Applicants container providers are meant to be looked up as
470    utilities. This way we can find all applicant container types
471    defined somewhere.
472
473    Each applicants container provider registered as utility provides
474    one container type and one should be able to call the `factory`
475    attribute to create an instance of the requested container type.
476
477    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
478
479    Samples:
480
481    Given, you had an IApplicantsContainer implementation somewhere
482    and you would like to make it findable on request, then you would
483    normally create an appropriate provider utility like this::
484
485      import grok
486      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
487
488      class MyContainerProvider(grok.GlobalUtility):
489          grok.implements(IApplicantsContainerProvider)
490          grok.name('MyContainerProvider') # Must be unique
491          factory = MyContainer # A class implementing IApplicantsContainer
492                                # or derivations thereof.
493
494    This utility would be registered on startup and could then be used
495    like this:
496
497      >>> from zope.component import getAllUtilitiesRegisteredFor
498      >>> from waeup.sirp.applicants.interfaces import (
499      ...     IApplicantsContainerProvider)
500      >>> all_providers = getAllUtilitiesRegisteredFor(
501      ...     IApplicantsContainerProvider)
502      >>> all_providers
503      [<MyContainerProvider object at 0x...>]
504
505    You could look up this specific provider by name:
506
507      >>> from zope.component import getUtility
508      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
509      >>> p
510      <MyContainerProvider object at 0x...>
511
512    An applicants container would then be created like this:
513
514      >>> provider = all_providers[0]
515      >>> container = provider.factory()
516      >>> container
517      <MyContainer object at 0x...>
518
519    """
520    factory = Attribute("A class that can create instances of the "
521                        "requested container type")
Note: See TracBrowser for help on using the repository browser.