source: main/waeup.sirp/branches/ulif-extimgstore/src/waeup/sirp/applicants/interfaces.py @ 7048

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

Remove obsolete imports and image field default provider.

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