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

Last change on this file since 6278 was 6256, checked in by Henrik Bettermann, 14 years ago

Move vocabularies and sources into a separate module like in university package. Unfortunately, ApplicantContainerProviderSource? can't be moved because it calls IApplicantsContainerProvider from the interface module.

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