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

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

Move lock field to the bottom.

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