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

Last change on this file since 6332 was 6332, checked in by uli, 13 years ago

Rename 'getApplicationState' to 'state'. Properties are always named like
attributes (lowe_case_with_underscores; they appear as attributes in the

interface) and not like methods (camelCase).

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