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

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

First part of acceptance fee payment integration (under construction).

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