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

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

Rebuild applicants package (1st part). Applicants now have an applicant_id and a password and can use the regular login page to enter the portal.

Add user_type attribute to SIRPPrincipal objects.

Add some permissions in students package.

Some tests are still missing and will be re-added soon.

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