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

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

Do not define workflow states as attributes. We will use the workflow adapters in views directly.

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 = 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    locked = schema.Bool(
289        title = u'Form locked',
290        default = False,
291        #readonly = True,
292        )
293
294    reg_no = schema.TextLine(
295        title = u'JAMB Registration Number',
296        readonly = True,
297        )
298    access_code = schema.TextLine(
299        title = u'Access Code',
300        required = False,
301        readonly = True,
302        )
303    course1 = schema.Choice(
304        title = u'1st Choice Course of Study',
305        source = AppCatCertificateSource(),
306        required = False,
307        )
308    course2 = schema.Choice(
309        title = u'2nd Choice Course of Study',
310        source = AppCatCertificateSource(),
311        required = False,
312        )
313    firstname = schema.TextLine(
314        title = u'First Name',
315        required = False,
316        )
317    middlenames = schema.TextLine(
318        title = u'Middle Names',
319        required = False,
320        )
321    lastname = schema.TextLine(
322        title = u'Last Name (Surname)',
323        required = False,
324        )
325    date_of_birth = schema.Date(
326        title = u'Date of Birth',
327        required = False,
328        )
329    lga = schema.Choice(
330        source = lgas_vocab,
331        title = u'State/LGA',
332        default = 'foreigner',
333        required = True,
334        )
335    sex = schema.Choice(
336        title = u'Sex',
337        source = GenderSource(),
338        default = u'm',
339        required = False,
340        )
341    email = schema.TextLine(
342        title = u'Email',
343        required = False,
344        )
345    phone = schema.TextLine(
346        title = u'Phone',
347        required = False,
348        )
349    passport = ImageFile(
350        title = u'Passport Photograph',
351        #default = DEFAULT_PASSPORT_IMAGE_MALE,
352        defaultFactory = default_passport_image,
353        required = True,
354        max_size = 20480,
355        )
356    confirm_passport = schema.Bool(
357        title = u"Passport picture confirmed",
358        default = False,
359        required = True,
360        )
361    #
362    # Process Data
363    #
364    application_date = schema.Date(
365        title = u'Application Date',
366        required = False,
367        readonly = True,
368        )
369    screening_score = schema.Int(
370        title = u'Screening Score',
371        required = False,
372        )
373    screening_venue = schema.TextLine(
374        title = u'Screening Venue',
375        required = False,
376        )
377    course_admitted = schema.Choice(
378        title = u'Admitted Course of Study',
379        source = CertificateSource(),
380        default = None,
381        required = False,
382        )
383    entry_session = schema.Choice(
384        source = entry_session_vocab,
385        title = u'Entry Session',
386        required = False,
387        )
388    notice = schema.Text(
389        title = u'Notice',
390        required = False,
391        )
392    student_id = schema.TextLine(
393        title = u'Student ID',
394        required = False,
395        readonly = True,
396        )
397
398class IApplicant(IApplicantBaseData):
399    """An applicant.
400
401    This is basically the applicant base data. Here we repeat the
402    fields from base data if we have to set the `required` attribute
403    to True (which is the default).
404    """
405
406class IApplicantEdit(IApplicantBaseData):
407    """An applicant.
408
409    Here we can repeat the fields from base data and set the `required` and
410    `readonly` attributes to True to further restrict the data access. We cannot
411    omit fields. This has to be done in the respective form page.
412    """
413    application_state = Attribute("The workflow state of an applicant object.")
414
415    screening_score = schema.Int(
416        title = u'Screening Score',
417        required = False,
418        readonly = True,
419        )
420    screening_venue = schema.TextLine(
421        title = u'Screening Venue',
422        required = False,
423        readonly = True,
424        )
425    course_admitted = schema.Choice(
426        title = u'Admitted Course of Study',
427        source = CertificateSource(),
428        default = None,
429        required = False,
430        readonly = True,
431        )
432    entry_session = schema.TextLine(
433        # XXX: should be choice
434        title = u'Entry Session',
435        required = False,
436        readonly = True,
437        )
438    notice = schema.Text(
439        title = u'Notice',
440        required = False,
441        readonly = True,
442        )
443    confirm_passport = schema.Bool(
444        title = u"I confirm that the Passport Photograph uploaded on this form is a true picture of me.",
445        default = False,
446        required = True,
447        )
448
449class IApplicantPrincipalInfo(IPrincipalInfo):
450    """Infos about principals that are applicants.
451    """
452    access_code = Attribute("The Access Code the user purchased")
453
454class IApplicantPrincipal(IPrincipal):
455    """A principal that is an applicant.
456
457    This interface extends zope.security.interfaces.IPrincipal and
458    requires also an `id` and other attributes defined there.
459    """
460    access_code = schema.TextLine(
461        title = u'Access Code',
462        description = u'The access code purchased by the user.',
463        required = True,
464        readonly = True)
465
466class IApplicantsFormChallenger(Interface):
467    """A challenger that uses a browser form to collect applicant
468       credentials.
469    """
470    loginpagename = schema.TextLine(
471        title = u'Loginpagename',
472        description = u"""Name of the login form used by challenger.
473
474        The form must provide an ``access_code`` input field.
475        """)
476
477    accesscode_field = schema.TextLine(
478        title = u'Access code field',
479        description = u'''Field of the login page which is looked up for
480                          access_code''',
481        default = u'access_code',
482        )
483
484
485class IApplicantSessionCredentials(Interface):
486    """Interface for storing and accessing applicant credentials in a
487       session.
488    """
489
490    def __init__(access_code):
491        """Create applicant session credentials."""
492
493    def getAccessCode():
494        """Return the access code."""
495
496
497class IApplicantsContainerProvider(Interface):
498    """A provider for applicants containers.
499
500    Applicants container providers are meant to be looked up as
501    utilities. This way we can find all applicant container types
502    defined somewhere.
503
504    Each applicants container provider registered as utility provides
505    one container type and one should be able to call the `factory`
506    attribute to create an instance of the requested container type.
507
508    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
509
510    Samples
511    *******
512
513    Given, you had an IApplicantsContainer implementation somewhere
514    and you would like to make it findable on request, then you would
515    normally create an appropriate provider utility like this::
516
517      import grok
518      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
519
520      class MyContainerProvider(grok.GlobalUtility):
521          grok.implements(IApplicantsContainerProvider)
522          grok.name('MyContainerProvider') # Must be unique
523          factory = MyContainer # A class implementing IApplicantsContainer
524                                # or derivations thereof.
525
526    This utility would be registered on startup and could then be used
527    like this:
528
529      >>> from zope.component import getAllUtilitiesRegisteredFor
530      >>> from waeup.sirp.applicants.interfaces import (
531      ...     IApplicantsContainerProvider)
532      >>> all_providers = getAllUtilitiesRegisteredFor(
533      ...     IApplicantsContainerProvider)
534      >>> all_providers
535      [<MyContainerProvider object at 0x...>]
536
537    You could look up this specific provider by name:
538
539      >>> from zope.component import getUtility
540      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
541      >>> p
542      <MyContainerProvider object at 0x...>
543
544    An applicants container would then be created like this:
545
546      >>> provider = all_providers[0]
547      >>> container = provider.factory()
548      >>> container
549      <MyContainer object at 0x...>
550
551    """
552    factory = Attribute("A class that can create instances of the "
553                        "requested container type")
Note: See TracBrowser for help on using the repository browser.