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

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

Set value Id for property svn:keywords in all Python files.

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