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

Last change on this file since 6313 was 6313, checked in by uli, 13 years ago
  • Make passport image placeholder constants strings instead of WAeUPImageFile instances. It turned out that defining WAeUPImageFile instances as constants gives some nasty (although most probably harmless) error messages at end of test runs.
  • Adapt the default value provider for passport fieds appropriately.
File size: 16.6 KB
Line 
1##
2## interfaces.py
3## Login : <uli@pu.smp.net>
4## Started on  Sun Jan 16 15:30:01 2011 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
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.
12##
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.
17##
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##
22"""Interfaces regarding student applicants and related components.
23"""
24import os
25import waeup.sirp.browser
26
27from grokcore.content.interfaces import IContainer
28
29from zope import schema
30from zope.interface import Interface, Attribute, provider
31from zope.component import getUtility, getUtilitiesFor
32from zope.pluggableauth.interfaces import IPrincipalInfo
33from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
34from zc.sourcefactory.basic import BasicSourceFactory
35from waeup.sirp.image.schema import ImageFile
36from waeup.sirp.image.image import WAeUPImageFile
37from waeup.sirp.interfaces import IWAeUPObject
38from waeup.sirp.university.vocabularies import application_categories
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  )
44
45IMAGE_PATH = os.path.join(
46    os.path.dirname(waeup.sirp.browser.__file__),
47    'static'
48    )
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()
53
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    """
68    return WAeUPImageFile(
69        'placeholder_m.jpg', DEFAULT_PASSPORT_IMAGE_MALE)
70
71class ApplicantContainerProviderSource(BasicSourceFactory):
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'
86
87    """
88    def getValues(self):
89        """Returns a list of ``(<name>, <provider>)`` tuples.
90
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
97    def getToken(self, value):
98        """Return the name of the ``(<name>, <provider>)`` tuple.
99        """
100        return value[0]
101
102    def getTitle(self, value):
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)
108
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
121class IApplicantsRoot(IWAeUPObject, IContainer):
122    """A container for university applicants containers.
123    """
124    pass
125
126class IApplicantsContainer(IWAeUPObject):
127    """An applicants container contains university applicants.
128
129    """
130
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
136
137    code = schema.TextLine(
138        title = u'Code',
139        default = u'-',
140        required = True,
141        readonly = True,
142        )
143
144    title = schema.TextLine(
145        title = u'Title',
146        required = True,
147        default = u'-',
148        readonly = True,
149        )
150
151    prefix = schema.Choice(
152        title = u'Application target',
153        required = True,
154        default = None,
155        source = application_types_vocab,
156        readonly = True,
157        )
158
159    year = schema.Choice(
160        title = u'Year of entrance',
161        required = True,
162        default = None,
163        values = year_range(),
164        readonly = True,
165        )
166
167    provider = schema.Choice(
168        title = u'Applicants container type',
169        required = True,
170        default = None,
171        source = ApplicantContainerProviderSource(),
172        readonly = True,
173        )
174
175    ac_prefix = schema.Choice(
176        title = u'Access code prefix',
177        required = True,
178        default = None,
179        source = application_pins_vocab,
180        )
181
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
189    description = schema.Text(
190        title = u'Human readable description in reST format',
191        required = False,
192        default = u'No description yet.'
193        )
194
195    startdate = schema.Date(
196        title = u'Date when the application period starts',
197        required = False,
198        default = None,
199        )
200
201    enddate = schema.Date(
202        title = u'Date when the application period ends',
203        required = False,
204        default = None,
205        )
206
207    strict_deadline = schema.Bool(
208        title = u'Forbid additions after deadline (enddate)',
209        required = True,
210        default = True,
211        )
212
213    def archive(id=None):
214        """Create on-dist archive of applicants stored in this term.
215
216        If id is `None`, all applicants are archived.
217
218        If id contains a single id string, only the respective
219        applicants are archived.
220
221        If id contains a list of id strings all of the respective
222        applicants types are saved to disk.
223        """
224
225    def clear(id=None, archive=True):
226        """Remove applicants of type given by 'id'.
227
228        Optionally archive the applicants.
229
230        If id is `None`, all applicants are archived.
231
232        If id contains a single id string, only the respective
233        applicants are archived.
234
235        If id contains a list of id strings all of the respective
236        applicant types are saved to disk.
237
238        If `archive` is ``False`` none of the archive-handling is done
239        and respective applicants are simply removed from the
240        database.
241        """
242
243class IApplicantsContainerAdd(IApplicantsContainer):
244    """An applicants container contains university applicants.
245    """
246    prefix = schema.Choice(
247        title = u'Application target',
248        required = True,
249        default = None,
250        source = application_types_vocab,
251        readonly = False,
252        )
253
254    year = schema.Choice(
255        title = u'Year of entrance',
256        required = True,
257        default = None,
258        values = year_range(),
259        readonly = False,
260        )
261
262    provider = schema.Choice(
263        title = u'Applicants container type',
264        required = True,
265        default = None,
266        source = ApplicantContainerProviderSource(),
267        readonly = False,
268        )
269
270IApplicantsContainerAdd[
271    'prefix'].order =  IApplicantsContainer['prefix'].order
272IApplicantsContainerAdd[
273    'year'].order =  IApplicantsContainer['year'].order
274IApplicantsContainerAdd[
275    'provider'].order =  IApplicantsContainer['provider'].order
276
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    """
285    messages = Attribute('Workflow transitions with date of transition.')
286
287    reg_no = schema.TextLine(
288        title = u'JAMB Registration Number',
289        readonly = True,
290        )
291    access_code = schema.TextLine(
292        title = u'Access Code',
293        required = False,
294        readonly = True,
295        )
296    course1 = schema.Choice(
297        title = u'1st Choice Course of Study',
298        source = AppCatCertificateSource(),
299        required = False,
300        )
301    course2 = schema.Choice(
302        title = u'2nd Choice Course of Study',
303        source = AppCatCertificateSource(),
304        required = False,
305        )
306    firstname = schema.TextLine(
307        title = u'First Name',
308        required = False,
309        )
310    middlenames = schema.TextLine(
311        title = u'Middle Names',
312        required = False,
313        )
314    lastname = schema.TextLine(
315        title = u'Last Name (Surname)',
316        required = False,
317        )
318    date_of_birth = schema.Date(
319        title = u'Date of Birth',
320        required = False,
321        )
322    lga = schema.Choice(
323        source = lgas_vocab,
324        title = u'State/LGA',
325        default = 'foreigner',
326        required = True,
327        )
328    sex = schema.Choice(
329        title = u'Sex',
330        source = GenderSource(),
331        default = u'm',
332        required = False,
333        )
334    email = schema.TextLine(
335        title = u'Email',
336        required = False,
337        )
338    phone = schema.TextLine(
339        title = u'Phone',
340        required = False,
341        )
342    passport = ImageFile(
343        title = u'Passport Photograph',
344        #default = DEFAULT_PASSPORT_IMAGE_MALE,
345        defaultFactory = default_passport_image,
346        required = True,
347        max_size = 20480,
348        )
349    confirm_passport = schema.Bool(
350        title = u"Passport picture confirmed",
351        default = False,
352        required = True,
353        )
354    #
355    # Process Data
356    #
357    application_date = schema.Date(
358        title = u'Application Date',
359        required = False,
360        readonly = True,
361        )
362    screening_score = schema.Int(
363        title = u'Screening Score',
364        required = False,
365        )
366    screening_venue = schema.TextLine(
367        title = u'Screening Venue',
368        required = False,
369        )
370    course_admitted = schema.Choice(
371        title = u'Admitted Course of Study',
372        source = CertificateSource(),
373        default = None,
374        required = False,
375        )
376    entry_session = schema.Choice(
377        source = entry_session_vocab,
378        title = u'Entry Session',
379        required = False,
380        )
381    notice = schema.Text(
382        title = u'Notice',
383        required = False,
384        )
385    student_id = schema.TextLine(
386        title = u'Student ID',
387        required = False,
388        readonly = True,
389        )
390    locked = schema.Bool(
391        title = u'Form locked',
392        default = False,
393        )
394
395class IApplicant(IApplicantBaseData):
396    """An applicant.
397
398    This is basically the applicant base data. Here we repeat the
399    fields from base data if we have to set the `required` attribute
400    to True (which is the default).
401    """
402
403class IApplicantEdit(IApplicantBaseData):
404    """An applicant.
405
406    Here we can repeat the fields from base data and set the `required` and
407    `readonly` attributes to True to further restrict the data access. We cannot
408    omit fields. This has to be done in the respective form page.
409    """
410    application_state = Attribute("The workflow state of an applicant object.")
411
412    screening_score = schema.Int(
413        title = u'Screening Score',
414        required = False,
415        readonly = True,
416        )
417    screening_venue = schema.TextLine(
418        title = u'Screening Venue',
419        required = False,
420        readonly = True,
421        )
422    course_admitted = schema.Choice(
423        title = u'Admitted Course of Study',
424        source = CertificateSource(),
425        default = None,
426        required = False,
427        readonly = True,
428        )
429    entry_session = schema.TextLine(
430        # XXX: should be choice
431        title = u'Entry Session',
432        required = False,
433        readonly = True,
434        )
435    notice = schema.Text(
436        title = u'Notice',
437        required = False,
438        readonly = True,
439        )
440    confirm_passport = schema.Bool(
441        title = u"I confirm that the Passport Photograph uploaded on this form is a true picture of me.",
442        default = False,
443        required = True,
444        )
445
446class IApplicantPrincipalInfo(IPrincipalInfo):
447    """Infos about principals that are applicants.
448    """
449    access_code = Attribute("The Access Code the user purchased")
450
451class IApplicantPrincipal(IPrincipal):
452    """A principal that is an applicant.
453
454    This interface extends zope.security.interfaces.IPrincipal and
455    requires also an `id` and other attributes defined there.
456    """
457    access_code = schema.TextLine(
458        title = u'Access Code',
459        description = u'The access code purchased by the user.',
460        required = True,
461        readonly = True)
462
463class IApplicantsFormChallenger(Interface):
464    """A challenger that uses a browser form to collect applicant
465       credentials.
466    """
467    loginpagename = schema.TextLine(
468        title = u'Loginpagename',
469        description = u"""Name of the login form used by challenger.
470
471        The form must provide an ``access_code`` input field.
472        """)
473
474    accesscode_field = schema.TextLine(
475        title = u'Access code field',
476        description = u'''Field of the login page which is looked up for
477                          access_code''',
478        default = u'access_code',
479        )
480
481
482class IApplicantSessionCredentials(Interface):
483    """Interface for storing and accessing applicant credentials in a
484       session.
485    """
486
487    def __init__(access_code):
488        """Create applicant session credentials."""
489
490    def getAccessCode():
491        """Return the access code."""
492
493
494class IApplicantsContainerProvider(Interface):
495    """A provider for applicants containers.
496
497    Applicants container providers are meant to be looked up as
498    utilities. This way we can find all applicant container types
499    defined somewhere.
500
501    Each applicants container provider registered as utility provides
502    one container type and one should be able to call the `factory`
503    attribute to create an instance of the requested container type.
504
505    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
506
507    Samples
508    *******
509
510    Given, you had an IApplicantsContainer implementation somewhere
511    and you would like to make it findable on request, then you would
512    normally create an appropriate provider utility like this::
513
514      import grok
515      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
516
517      class MyContainerProvider(grok.GlobalUtility):
518          grok.implements(IApplicantsContainerProvider)
519          grok.name('MyContainerProvider') # Must be unique
520          factory = MyContainer # A class implementing IApplicantsContainer
521                                # or derivations thereof.
522
523    This utility would be registered on startup and could then be used
524    like this:
525
526      >>> from zope.component import getAllUtilitiesRegisteredFor
527      >>> from waeup.sirp.applicants.interfaces import (
528      ...     IApplicantsContainerProvider)
529      >>> all_providers = getAllUtilitiesRegisteredFor(
530      ...     IApplicantsContainerProvider)
531      >>> all_providers
532      [<MyContainerProvider object at 0x...>]
533
534    You could look up this specific provider by name:
535
536      >>> from zope.component import getUtility
537      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
538      >>> p
539      <MyContainerProvider object at 0x...>
540
541    An applicants container would then be created like this:
542
543      >>> provider = all_providers[0]
544      >>> container = provider.factory()
545      >>> container
546      <MyContainer object at 0x...>
547
548    """
549    factory = Attribute("A class that can create instances of the "
550                        "requested container type")
Note: See TracBrowser for help on using the repository browser.