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

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

Move workflow methods from certificates to applicants.

File size: 16.7 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
370    #application_state = schema.Choice(
371    #    title = u'Application Status',
372    #    default = 'created',
373    #    values = ['unchecked', 'checked']
374    #    readonly = True,
375    #    )
376
377    screening_score = schema.Int(
378        title = u'Screening Score',
379        required = False,
380        )
381    screening_venue = schema.TextLine(
382        title = u'Screening Venue',
383        required = False,
384        )
385    course_admitted = schema.Choice(
386        title = u'Admitted Course of Study',
387        source = CertificateSource(),
388        default = None,
389        required = False,
390        )
391    entry_session = schema.Choice(
392        source = entry_session_vocab,
393        title = u'Entry Session',
394        required = False,
395        )
396    notice = schema.Text(
397        title = u'Notice',
398        required = False,
399        )
400    student_id = schema.TextLine(
401        title = u'Student ID',
402        required = False,
403        readonly = True,
404        )
405
406class IApplicant(IApplicantBaseData):
407    """An applicant.
408
409    This is basically the applicant base data. Here we repeat the
410    fields from base data if we have to set the `required` attribute
411    to True (which is the default).
412    """
413
414class IApplicantEdit(IApplicantBaseData):
415    """An applicant.
416
417    Here we can repeat the fields from base data and set the `required` and
418    `readonly` attributes to True to further restrict the data access. We cannot
419    omit fields. This has to be done in the respective form page.
420    """
421    screening_score = schema.Int(
422        title = u'Screening Score',
423        required = False,
424        readonly = True,
425        )
426    screening_venue = schema.TextLine(
427        title = u'Screening Venue',
428        required = False,
429        readonly = True,
430        )
431    course_admitted = schema.TextLine(
432        # XXX: should be choice
433        title = u'Admitted Course of Study',
434        required = False,
435        readonly = True,
436        default = None,
437        )
438    entry_session = schema.TextLine(
439        # XXX: should be choice
440        title = u'Entry Session',
441        required = False,
442        readonly = True,
443        )
444    notice = schema.Text(
445        title = u'Notice',
446        required = False,
447        readonly = True,
448        )
449    confirm_passport = schema.Bool(
450        title = u"I confirm that the Passport Photograph uploaded on this form is a true picture of me.",
451        default = False,
452        required = True,
453        )
454
455class IApplicantPrincipalInfo(IPrincipalInfo):
456    """Infos about principals that are applicants.
457    """
458    access_code = Attribute("The Access Code the user purchased")
459
460class IApplicantPrincipal(IPrincipal):
461    """A principal that is an applicant.
462
463    This interface extends zope.security.interfaces.IPrincipal and
464    requires also an `id` and other attributes defined there.
465    """
466    access_code = schema.TextLine(
467        title = u'Access Code',
468        description = u'The access code purchased by the user.',
469        required = True,
470        readonly = True)
471
472class IApplicantsFormChallenger(Interface):
473    """A challenger that uses a browser form to collect applicant
474       credentials.
475    """
476    loginpagename = schema.TextLine(
477        title = u'Loginpagename',
478        description = u"""Name of the login form used by challenger.
479
480        The form must provide an ``access_code`` input field.
481        """)
482
483    accesscode_field = schema.TextLine(
484        title = u'Access code field',
485        description = u'''Field of the login page which is looked up for
486                          access_code''',
487        default = u'access_code',
488        )
489
490
491class IApplicantSessionCredentials(Interface):
492    """Interface for storing and accessing applicant credentials in a
493       session.
494    """
495
496    def __init__(access_code):
497        """Create applicant session credentials."""
498
499    def getAccessCode():
500        """Return the access code."""
501
502
503class IApplicantsContainerProvider(Interface):
504    """A provider for applicants containers.
505
506    Applicants container providers are meant to be looked up as
507    utilities. This way we can find all applicant container types
508    defined somewhere.
509
510    Each applicants container provider registered as utility provides
511    one container type and one should be able to call the `factory`
512    attribute to create an instance of the requested container type.
513
514    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
515
516    Samples
517    *******
518
519    Given, you had an IApplicantsContainer implementation somewhere
520    and you would like to make it findable on request, then you would
521    normally create an appropriate provider utility like this::
522
523      import grok
524      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
525
526      class MyContainerProvider(grok.GlobalUtility):
527          grok.implements(IApplicantsContainerProvider)
528          grok.name('MyContainerProvider') # Must be unique
529          factory = MyContainer # A class implementing IApplicantsContainer
530                                # or derivations thereof.
531
532    This utility would be registered on startup and could then be used
533    like this:
534
535      >>> from zope.component import getAllUtilitiesRegisteredFor
536      >>> from waeup.sirp.applicants.interfaces import (
537      ...     IApplicantsContainerProvider)
538      >>> all_providers = getAllUtilitiesRegisteredFor(
539      ...     IApplicantsContainerProvider)
540      >>> all_providers
541      [<MyContainerProvider object at 0x...>]
542
543    You could look up this specific provider by name:
544
545      >>> from zope.component import getUtility
546      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
547      >>> p
548      <MyContainerProvider object at 0x...>
549
550    An applicants container would then be created like this:
551
552      >>> provider = all_providers[0]
553      >>> container = provider.factory()
554      >>> container
555      <MyContainer object at 0x...>
556
557    """
558    factory = Attribute("A class that can create instances of the "
559                        "requested container type")
Note: See TracBrowser for help on using the repository browser.