source: main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py @ 8013

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

Remove ApplicantContainerProviderSource? too.

  • Property svn:keywords set to Id
File size: 15.1 KB
RevLine 
[5638]1## $Id: interfaces.py 8010 2012-03-30 10:32:27Z henrik $
[6076]2##
[6087]3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
[5638]4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
[6076]8##
[5638]9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
[6076]13##
[5638]14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
[6500]18"""Interfaces of the university application package.
[5638]19"""
[5866]20from grokcore.content.interfaces import IContainer
[7795]21from zc.sourcefactory.basic import BasicSourceFactory
22from zc.sourcefactory.contextual import BasicContextualSourceFactory
[5638]23from zope import schema
[7683]24from zope.component import getUtilitiesFor, queryUtility, getUtility
[7263]25from zope.catalog.interfaces import ICatalog
[7795]26from zope.interface import Interface, Attribute, implements, directlyProvides
[7317]27from zope.schema.interfaces import (
28    ValidationError, ISource, IContextSourceBinder)
[7811]29from waeup.kofa.schema import TextLineChoice
30from waeup.kofa.interfaces import (
[7819]31    IKofaObject, year_range, validate_email, academic_sessions_vocab)
[7811]32from waeup.kofa.interfaces import MessageFactory as _
33from waeup.kofa.payments.interfaces import IOnlinePayment
34from waeup.kofa.schoolgrades import ResultEntryField
35from waeup.kofa.students.vocabularies import (
[7915]36    lgas_vocab, GenderSource)
[7811]37from waeup.kofa.university.vocabularies import (
[7915]38    course_levels, AppCatSource, CertificateSource)
[5638]39
[7075]40#: Maximum upload size for applicant passport photographs (in bytes)
[7086]41MAX_UPLOAD_SIZE = 1024 * 20
[7075]42
[7263]43class RegNumInSource(ValidationError):
44    """Registration number exists already
45    """
46    # The docstring of ValidationErrors is used as error description
47    # by zope.formlib.
48    pass
49
50class RegNumberSource(object):
51    implements(ISource)
52    cat_name = 'applicants_catalog'
[7270]53    field_name = 'reg_number'
[7263]54    validation_error = RegNumInSource
55    def __init__(self, context):
56        self.context = context
57        return
58
59    def __contains__(self, value):
60        cat = queryUtility(ICatalog, self.cat_name)
61        if cat is None:
62            return True
63        kw = {self.field_name: (value, value)}
64        results = cat.searchResults(**kw)
65        for entry in results:
66            if entry.applicant_id != self.context.applicant_id:
67                # XXX: sources should simply return False.
68                #      But then we get some stupid error message in forms
69                #      when validation fails.
70                raise self.validation_error(value)
71                #return False
72        return True
73
74def contextual_reg_num_source(context):
75    source = RegNumberSource(context)
76    return source
77directlyProvides(contextual_reg_num_source, IContextSourceBinder)
78
[7795]79
[7683]80class AppCatCertificateSource(CertificateSource):
81    """An application certificate source delivers all courses which belong to
82    a certain application_category.
83    """
84    def getValues(self, context):
85        # appliction category not available when certificate was deleted.
86        # shouldn't that info be part of applicant info instead?
87        # when we cannot determine the appcat, we will display all courses.
88        appcat = getattr(getattr(context, '__parent__', None),
89                         'application_category', None)
90        catalog = getUtility(ICatalog, name='certificates_catalog')
91        result = catalog.searchResults(
92            application_category=(appcat,appcat))
93        result = sorted(result, key=lambda value: value.code)
94        curr_course = context.course1
95        if curr_course is not None and curr_course not in result:
96            # display also current course even if it is not catalogued
97            # (any more)
98            result = [curr_course,] + result
99        return result
100
101class ApplicationTypeSource(BasicContextualSourceFactory):
102    """An application type source delivers screening types defined in the
103    portal.
104    """
105    def getValues(self, context):
[7688]106        appcats_dict = getUtility(
[7844]107            IApplicantsUtils).APP_TYPES_DICT
[7688]108        return sorted(appcats_dict.keys())
[7683]109
110    def getToken(self, context, value):
111        return value
112
113    def getTitle(self, context, value):
[7688]114        appcats_dict = getUtility(
[7844]115            IApplicantsUtils).APP_TYPES_DICT
[7688]116        return appcats_dict[value][0]
[7683]117
118# Maybe Uniben still needs this ...
119#class ApplicationPinSource(BasicContextualSourceFactory):
120#    """An application pin source delivers PIN prefixes for application
121#    defined in the portal.
122#    """
123#    def getValues(self, context):
[7688]124#        apppins_dict = getUtility(
[7844]125#            IApplicantsUtils).APP_TYPES_DICT
[7688]126#        return sorted(appcats_dict.keys())
[7683]127#
128#    def getToken(self, context, value):
129#        return value
130#
131#    def getTitle(self, context, value):
[7688]132#        apppins_dict = getUtility(
[7844]133#            IApplicantsUtils).APP_TYPES_DICT
[7683]134#        return u"%s (%s)" % (
[7688]135#            apppins_dict[value][1],self.apppins_dict[value][0])
[7683]136
[6075]137
[7682]138class IApplicantsUtils(Interface):
139    """A collection of methods which are subject to customization.
140    """
141
[7844]142    APP_TYPES_DICT = Attribute(' dict of application types')
143
[7819]144class IApplicantsRoot(IKofaObject, IContainer):
[5676]145    """A container for university applicants containers.
[5645]146    """
[5866]147    pass
[5638]148
[7819]149class IApplicantsContainer(IKofaObject):
[5676]150    """An applicants container contains university applicants.
[5645]151
[5638]152    """
[6069]153
154    code = schema.TextLine(
[7708]155        title = _(u'Code'),
[6069]156        required = True,
157        readonly = True,
[6076]158        )
[6096]159
[6087]160    title = schema.TextLine(
[7708]161        title = _(u'Title'),
[6087]162        required = True,
163        readonly = True,
[6096]164        )
165
[6087]166    prefix = schema.Choice(
[7708]167        title = _(u'Application Target'),
[6087]168        required = True,
[7683]169        source = ApplicationTypeSource(),
[6087]170        readonly = True,
171        )
[6076]172
[6087]173    year = schema.Choice(
[7708]174        title = _(u'Year of Entrance'),
[6087]175        required = True,
[6158]176        values = year_range(),
[6087]177        readonly = True,
[6096]178        )
[6087]179
[8008]180    entry_level = schema.Choice(
181        title = _(u'Entry Level'),
182        vocabulary = course_levels,
[6069]183        required = True,
[6076]184        )
[6158]185
[7683]186    # Maybe Uniben still needs this ...
[7376]187    #ac_prefix = schema.Choice(
188    #    title = u'Activation code prefix',
189    #    required = True,
190    #    default = None,
[7683]191    #    source = ApplicationPinSource(),
[7376]192    #    )
[6076]193
[6189]194    application_category = schema.Choice(
[7708]195        title = _(u'Category for the grouping of certificates'),
[6189]196        required = True,
[7681]197        source = AppCatSource(),
[6189]198        )
199
[5645]200    description = schema.Text(
[7708]201        title = _(u'Human readable description in reST format'),
[5638]202        required = False,
[6518]203        default = u'''This text can been seen by anonymous users.
[7708]204Here we put mult-lingual information about the study courses provided, the application procedure and deadlines.
205>>de<<
206Dieser Text kann von anonymen Benutzern gelesen werden.
207Hier koennen mehrsprachige Informationen fuer Antragsteller hinterlegt werden.'''
[5638]208        )
209
[7903]210    description_dict = Attribute(
211        """Content as language dictionary with values in HTML format.""")
[7708]212
[5638]213    startdate = schema.Date(
[7708]214        title = _(u'Application Start Date'),
[5638]215        required = False,
216        )
217
218    enddate = schema.Date(
[7708]219        title = _(u'Application Closing Date'),
[5638]220        required = False,
221        )
222
[5645]223    strict_deadline = schema.Bool(
[7708]224        title = _(u'Forbid additions after deadline (enddate)'),
[7984]225        required = False,
[5645]226        default = True,
227        )
[5638]228
229    def archive(id=None):
[5676]230        """Create on-dist archive of applicants stored in this term.
[5638]231
[5676]232        If id is `None`, all applicants are archived.
[5638]233
234        If id contains a single id string, only the respective
[5676]235        applicants are archived.
[5638]236
237        If id contains a list of id strings all of the respective
[5676]238        applicants types are saved to disk.
[5638]239        """
240
241    def clear(id=None, archive=True):
[5676]242        """Remove applicants of type given by 'id'.
[5638]243
[5676]244        Optionally archive the applicants.
[6076]245
[5676]246        If id is `None`, all applicants are archived.
[5638]247
248        If id contains a single id string, only the respective
[5676]249        applicants are archived.
[5638]250
251        If id contains a list of id strings all of the respective
[5676]252        applicant types are saved to disk.
[5638]253
254        If `archive` is ``False`` none of the archive-handling is done
[5676]255        and respective applicants are simply removed from the
[5638]256        database.
257        """
[6073]258
[6069]259class IApplicantsContainerAdd(IApplicantsContainer):
260    """An applicants container contains university applicants.
261    """
[6087]262    prefix = schema.Choice(
[7708]263        title = _(u'Application Target'),
[6069]264        required = True,
[7683]265        source = ApplicationTypeSource(),
[6069]266        readonly = False,
[6076]267        )
[6073]268
[6087]269    year = schema.Choice(
[7708]270        title = _(u'Year of Entrance'),
[6087]271        required = True,
[6158]272        values = year_range(),
[6087]273        readonly = False,
[6096]274        )
[6073]275
[6096]276IApplicantsContainerAdd[
277    'prefix'].order =  IApplicantsContainer['prefix'].order
278IApplicantsContainerAdd[
279    'year'].order =  IApplicantsContainer['year'].order
[6087]280
[7819]281class IApplicantBaseData(IKofaObject):
[5753]282    """The data for an applicant.
283
[7240]284    This is a base interface with no field
[7933]285    required. For use with processors, forms, etc., please use one of
[5753]286    the derived interfaces below, which set more fields to required
287    state, depending on use-case.
[7338]288
[7795]289    This base interface is also implemented by the
[7811]290    :class:`waeup.kofa.students.StudentApplication` class in the
[7795]291    students package. Thus, these are the data which are saved after
292    admission.
[5753]293    """
[7240]294    applicant_id = schema.TextLine(
[7708]295        title = _(u'Applicant Id'),
[7240]296        required = False,
[7260]297        readonly = False,
[7240]298        )
[7270]299    reg_number = TextLineChoice(
[7708]300        title = _(u'JAMB Registration Number'),
[7263]301        readonly = False,
302        required = True,
303        source = contextual_reg_num_source,
[5753]304        )
[7376]305    #access_code = schema.TextLine(
306    #    title = u'Activation Code',
307    #    required = False,
308    #    readonly = True,
309    #    )
[5753]310    firstname = schema.TextLine(
[7708]311        title = _(u'First Name'),
[6352]312        required = True,
[5753]313        )
[7356]314    middlename = schema.TextLine(
[7708]315        title = _(u'Middle Name'),
[5753]316        required = False,
317        )
318    lastname = schema.TextLine(
[7708]319        title = _(u'Last Name (Surname)'),
[6352]320        required = True,
[5753]321        )
322    date_of_birth = schema.Date(
[7708]323        title = _(u'Date of Birth'),
[6352]324        required = True,
[5753]325        )
[6249]326    lga = schema.Choice(
327        source = lgas_vocab,
[7708]328        title = _(u'State/LGA'),
[6254]329        default = 'foreigner',
[7984]330        required = False,
[5753]331        )
332    sex = schema.Choice(
[7708]333        title = _(u'Sex'),
[5753]334        source = GenderSource(),
[6352]335        required = True,
[5753]336        )
[6341]337    email = schema.ASCIILine(
[7708]338        title = _(u'Email Address'),
[7240]339        required = True,
[6343]340        constraint=validate_email,
[5753]341        )
[7324]342    phone = schema.TextLine(
[7708]343        title = _(u'Phone'),
[7331]344        description = u'',
[5753]345        required = False,
346        )
[7262]347    course1 = schema.Choice(
[7708]348        title = _(u'1st Choice Course of Study'),
[7347]349        source = CertificateSource(),
[7262]350        required = True,
351        )
352    course2 = schema.Choice(
[7708]353        title = _(u'2nd Choice Course of Study'),
[7347]354        source = CertificateSource(),
[7262]355        required = False,
356        )
[7795]357    school_grades = schema.List(
358        title = _(u'School Grades'),
359        value_type = ResultEntryField(),
[7984]360        required = False,
[7795]361        default = [],
362        )
[6322]363
[5753]364    #
[7338]365    # Data to be imported after screening
[5753]366    #
[6255]367    screening_score = schema.Int(
[7708]368        title = _(u'Screening Score'),
[5753]369        required = False,
370        )
371    screening_venue = schema.TextLine(
[7708]372        title = _(u'Screening Venue'),
[5753]373        required = False,
374        )
[6248]375    course_admitted = schema.Choice(
[7708]376        title = _(u'Admitted Course of Study'),
[7347]377        source = CertificateSource(),
[5753]378        required = False,
379        )
[7347]380    notice = schema.Text(
[7708]381        title = _(u'Notice'),
[7347]382        required = False,
383        )
[7338]384
385class IApplicantProcessData(IApplicantBaseData):
386    """An applicant.
387
388    Here we add process attributes and methods to the base data.
389    """
390
[7844]391    history = Attribute('Object history, a list of messages')
[7338]392    state = Attribute('The application state of an applicant')
[7364]393    display_fullname = Attribute('The fullname of an applicant')
[7338]394    application_date = Attribute('Date of submission, used for export only')
395    password = Attribute('Encrypted password of a applicant')
396    application_number = Attribute('The key under which the record is stored')
397
398    def loggerInfo(ob_class, comment):
399        """Adds an INFO message to the log file
400        """
401
[5753]402    student_id = schema.TextLine(
[7708]403        title = _(u'Student Id'),
[5753]404        required = False,
[7351]405        readonly = False,
[5753]406        )
[6302]407    locked = schema.Bool(
[7708]408        title = _(u'Form locked'),
[6302]409        default = False,
410        )
[5753]411
[7338]412class IApplicant(IApplicantProcessData):
[5753]413    """An applicant.
414
415    This is basically the applicant base data. Here we repeat the
[6195]416    fields from base data if we have to set the `required` attribute
417    to True (which is the default).
[5753]418    """
419
[7338]420class IApplicantEdit(IApplicantProcessData):
[7867]421    """An applicant interface for editing.
[5753]422
[6339]423    Here we can repeat the fields from base data and set the
424    `required` and `readonly` attributes to True to further restrict
[7262]425    the data access. Or we can allow only certain certificates to be
426    selected by choosing the appropriate source.
427
428    We cannot omit fields here. This has to be done in the
[6339]429    respective form page.
[6195]430    """
[7262]431
432    course1 = schema.Choice(
[7708]433        title = _(u'1st Choice Course of Study'),
[7347]434        source = AppCatCertificateSource(),
[7262]435        required = True,
436        )
437    course2 = schema.Choice(
[7708]438        title = _(u'2nd Choice Course of Study'),
[7347]439        source = AppCatCertificateSource(),
[7262]440        required = False,
441        )
[6255]442    screening_score = schema.Int(
[7708]443        title = _(u'Screening Score'),
[6195]444        required = False,
[5941]445        readonly = True,
446        )
[6195]447    screening_venue = schema.TextLine(
[7708]448        title = _(u'Screening Venue'),
[5753]449        required = False,
450        readonly = True,
451        )
[6301]452    course_admitted = schema.Choice(
[7708]453        title = _(u'Admitted Course of Study'),
[7347]454        source = CertificateSource(),
[5753]455        required = False,
[6195]456        readonly = True,
[5753]457        )
[6195]458    notice = schema.Text(
[7708]459        title = _(u'Notice'),
[5753]460        required = False,
461        readonly = True,
462        )
[5758]463
[7338]464    def createStudent():
465        """Create a student object from applicatnt data
466        and copy applicant object.
467        """
468
[7268]469class IApplicantUpdateByRegNo(IApplicant):
470    """Representation of an applicant.
471
[7270]472    Skip regular reg_number validation if reg_number is used for finding
[7268]473    the applicant object.
474    """
[7270]475    reg_number = schema.TextLine(
[7268]476        title = u'Registration Number',
477        required = False,
478        )
479
[7250]480class IApplicantOnlinePayment(IOnlinePayment):
481    """An applicant payment via payment gateways.
482
483    """
484    p_year = schema.Choice(
[7708]485        title = _(u'Payment Session'),
[7250]486        source = academic_sessions_vocab,
487        required = False,
488        )
489
490IApplicantOnlinePayment['p_year'].order = IApplicantOnlinePayment[
491    'p_year'].order
492
Note: See TracBrowser for help on using the repository browser.