## ## interfaces.py ## Login : ## Started on Sun Jan 16 15:30:01 2011 Uli Fouquet ## $Id$ ## ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## """Interfaces regarding student applicants and related components. """ import os import waeup.sirp.browser from datetime import datetime from grokcore.content.interfaces import IContainer from zc.sourcefactory.basic import BasicSourceFactory from zope import schema from zope.component import getUtility, getUtilitiesFor from zope.interface import Interface, Attribute from zope.pluggableauth.interfaces import IPrincipalInfo from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal from waeup.sirp.image.schema import ImageFile from waeup.sirp.image.image import WAeUPImageFile from waeup.sirp.interfaces import IWAeUPObject, SimpleWAeUPVocabulary from waeup.sirp.university.vocabularies import application_categories IMAGE_PATH = os.path.join( os.path.dirname(waeup.sirp.browser.__file__), 'static' ) DEFAULT_PASSPORT_IMAGE_MALE = WAeUPImageFile( 'passport.jpg', open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg')).read(), ) DEFAULT_PASSPORT_IMAGE_FEMALE = WAeUPImageFile( 'passport.jpg', open(os.path.join(IMAGE_PATH, 'placeholder_f.jpg')).read(), ) #: Types of applications we support. APPLICATION_TYPES = ( ('General Studies', 'app','APP'), ('Pre-NCE Programme', 'prence','PRE'), ('Post UME Screening Test', 'pume','PUME'), ('Post UDE Screening', 'pude','PUDE'), ('Part Time Degree in Education', 'sandwich','SAND'), ('Part-Time Degree Programmes', 'pt','PTP'), ('Diploma Programmes', 'dp','DPP'), ('PCE Screening', 'pce','PCE'), ('Certificate Programmes', 'ct','CTP'), ('Common Entry Screening Test', 'cest','CEST'), ) #: A :class:`waeup.sirp.interfaces.SimpleWAeUPVocabulary` of supported #: application or screening types. application_types_vocab = SimpleWAeUPVocabulary( *[(x[0],x[1]) for x in APPLICATION_TYPES]) application_pins_vocab = SimpleWAeUPVocabulary( *[(u"%s (%s)" % (x[2],x[0]),x[2]) for x in APPLICATION_TYPES]) def year_range(): curr_year = datetime.now().year return range(curr_year - 2, curr_year + 5) class GenderSource(BasicSourceFactory): """A gender source delivers basically a mapping ``{'m': 'male', 'f': 'female'}`` Using a source, we make sure that the tokens (which are stored/expected for instance from CSV files) are something one can expect and not cryptic IntIDs. """ def getValues(self): return ['m', 'f'] def getToken(self, value): return value[0].lower() def getTitle(self, value): if value == 'm': return 'male' if value == 'f': return 'female' class ApplicantContainerProviderSource(BasicSourceFactory): """A source offering all available applicants container types. The values returned by this source are names of utilities that can create :class:`ApplicantContainer` instances. So, if you get a name like ``'myactype'`` from this source, then you can do: >>> from zope.component import getUtility >>> p = getUtility(IApplicantsContainerProvider, name=myactype) >>> my_applicants_container = p.factory() Or you can access class-attributes like >>> my_applicants_container.container_title 'Pretty' """ def getValues(self): """Returns a list of ``(, )`` tuples. Here ```` is the name under which an :class:``ApplicantContainerProvider`` was registered as a utility and ```` is the utility itself. """ return getUtilitiesFor(IApplicantsContainerProvider) def getToken(self, value): """Return the name of the ``(, )`` tuple. """ return value[0] def getTitle(self, value): """Get a 'title - description' string for a container type. """ factory = value[1].factory return "%s - %s" % ( factory.container_title, factory.container_description) class IResultEntry(IWAeUPObject): subject = schema.TextLine( title = u'Subject', description = u'The subject', required=False, ) score = schema.TextLine( title = u'Score', description = u'The score', required=False, ) class IApplicantsRoot(IWAeUPObject, IContainer): """A container for university applicants containers. """ pass class IApplicantsContainer(IWAeUPObject): """An applicants container contains university applicants. """ container_title = Attribute( u'classattribute: title for type of container') container_description = Attribute( u'classattribute: description for type of container') code = schema.TextLine( title = u'Code', default = u'-', required = True, readonly = True, ) title = schema.TextLine( title = u'Title', required = True, default = u'-', readonly = True, ) prefix = schema.Choice( title = u'Application target', required = True, default = None, source = application_types_vocab, readonly = True, ) year = schema.Choice( title = u'Year of entrance', required = True, default = None, values = year_range(), readonly = True, ) provider = schema.Choice( title = u'Applicants container type', required = True, default = None, source = ApplicantContainerProviderSource(), readonly = True, ) ac_prefix = schema.Choice( title = u'Access code prefix', required = True, default = None, source = application_pins_vocab, ) application_category = schema.Choice( title = u'Category for the grouping of study courses', required = True, default = None, source = application_categories, ) description = schema.Text( title = u'Human readable description in reST format', required = False, default = u'No description yet.' ) startdate = schema.Date( title = u'Date when the application period starts', required = False, default = None, ) enddate = schema.Date( title = u'Date when the application period ends', required = False, default = None, ) strict_deadline = schema.Bool( title = u'Forbid additions after deadline (enddate)', required = True, default = True, ) def archive(id=None): """Create on-dist archive of applicants stored in this term. If id is `None`, all applicants are archived. If id contains a single id string, only the respective applicants are archived. If id contains a list of id strings all of the respective applicants types are saved to disk. """ def clear(id=None, archive=True): """Remove applicants of type given by 'id'. Optionally archive the applicants. If id is `None`, all applicants are archived. If id contains a single id string, only the respective applicants are archived. If id contains a list of id strings all of the respective applicant types are saved to disk. If `archive` is ``False`` none of the archive-handling is done and respective applicants are simply removed from the database. """ class IApplicantsContainerAdd(IApplicantsContainer): """An applicants container contains university applicants. """ prefix = schema.Choice( title = u'Application target', required = True, default = None, source = application_types_vocab, readonly = False, ) year = schema.Choice( title = u'Year of entrance', required = True, default = None, values = year_range(), readonly = False, ) provider = schema.Choice( title = u'Applicants container type', required = True, default = None, source = ApplicantContainerProviderSource(), readonly = False, ) IApplicantsContainerAdd[ 'prefix'].order = IApplicantsContainer['prefix'].order IApplicantsContainerAdd[ 'year'].order = IApplicantsContainer['year'].order IApplicantsContainerAdd[ 'provider'].order = IApplicantsContainer['provider'].order class IApplicantBaseData(IWAeUPObject): """The data for an applicant. This is a base interface with no field (except ``reg_no``) required. For use with importers, forms, etc., please use one of the derived interfaces below, which set more fields to required state, depending on use-case. """ locked = schema.Bool( title = u'Form locked', default = False, #readonly = True, ) reg_no = schema.TextLine( title = u'JAMB Registration Number', readonly = True, ) access_code = schema.TextLine( title = u'Access Code', required = False, readonly = True, ) course1 = schema.TextLine( # XXX: should be choice title = u'1st Choice Course of Study', required = False, ) course2 = schema.TextLine( # XXX: should be choice title = u'2nd Choice Course of Study', required = False, ) firstname = schema.TextLine( title = u'First Name', required = False, ) middlenames = schema.TextLine( title = u'Middle Names', required = False, ) lastname = schema.TextLine( title = u'Last Name (Surname)', required = False, ) date_of_birth = schema.Date( title = u'Date of Birth', required = False, ) lga = schema.TextLine( # XXX: should be choice title = u'State/LGA', required = False, ) sex = schema.Choice( title = u'Sex', source = GenderSource(), default = u'm', required = False, ) email = schema.TextLine( title = u'Email', required = False, ) phone = schema.TextLine( title = u'Phone', required = False, ) passport = ImageFile( title = u'Passport Photograph', default = DEFAULT_PASSPORT_IMAGE_MALE, required = True, #max_size = 20480, ) confirm_passport = schema.Bool( title = u"Passport picture confirmed", default = False, required = True, ) # # Process Data # application_date = schema.Date( title = u'Application Date', required = False, readonly = True, ) status = schema.TextLine( # XXX: should be 'status' type title = u'Application Status', required = False, readonly = True, ) screening_score = schema.TextLine( title = u'Screening Score', required = False, ) screening_venue = schema.TextLine( title = u'Screening Venue', required = False, ) course_admitted = schema.TextLine( # XXX: should be choice title = u'Admitted Course of Study', required = False, ) entry_session = schema.TextLine( # XXX: should be choice title = u'Entry Session', required = False, ) notice = schema.Text( title = u'Notice', required = False, ) student_id = schema.TextLine( title = u'Student ID', required = False, readonly = True, ) class IApplicant(IApplicantBaseData): """An applicant. This is basically the applicant base data. Here we repeat the fields from base data if we have to set the `required` attribute to True (which is the default). """ class IApplicantEdit(IApplicantBaseData): """An applicant. Here we can repeat the fields from base data and set the `required` and `readonly` attributes to True to further restrict the data access. We cannot omit fields. This has to be done in the respective form page. """ screening_score = schema.TextLine( title = u'Screening Score', required = False, readonly = True, ) screening_venue = schema.TextLine( title = u'Screening Venue', required = False, readonly = True, ) course_admitted = schema.TextLine( # XXX: should be choice title = u'Admitted Course of Study', required = False, readonly = True, ) entry_session = schema.TextLine( # XXX: should be choice title = u'Entry Session', required = False, readonly = True, ) notice = schema.Text( title = u'Notice', required = False, readonly = True, ) confirm_passport = schema.Bool( title = u"I confirm that the Passport Photograph uploaded on this form is a true picture of me.", default = False, required = True, ) class IApplicantPrincipalInfo(IPrincipalInfo): """Infos about principals that are applicants. """ access_code = Attribute("The Access Code the user purchased") class IApplicantPrincipal(IPrincipal): """A principal that is an applicant. This interface extends zope.security.interfaces.IPrincipal and requires also an `id` and other attributes defined there. """ access_code = schema.TextLine( title = u'Access Code', description = u'The access code purchased by the user.', required = True, readonly = True) class IApplicantsFormChallenger(Interface): """A challenger that uses a browser form to collect applicant credentials. """ loginpagename = schema.TextLine( title = u'Loginpagename', description = u"""Name of the login form used by challenger. The form must provide an ``access_code`` input field. """) accesscode_field = schema.TextLine( title = u'Access code field', description = u'''Field of the login page which is looked up for access_code''', default = u'access_code', ) class IApplicantSessionCredentials(Interface): """Interface for storing and accessing applicant credentials in a session. """ def __init__(access_code): """Create applicant session credentials.""" def getAccessCode(): """Return the access code.""" class IApplicantsContainerProvider(Interface): """A provider for applicants containers. Applicants container providers are meant to be looked up as utilities. This way we can find all applicant container types defined somewhere. Each applicants container provider registered as utility provides one container type and one should be able to call the `factory` attribute to create an instance of the requested container type. .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested) Samples ******* Given, you had an IApplicantsContainer implementation somewhere and you would like to make it findable on request, then you would normally create an appropriate provider utility like this:: import grok from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider class MyContainerProvider(grok.GlobalUtility): grok.implements(IApplicantsContainerProvider) grok.name('MyContainerProvider') # Must be unique factory = MyContainer # A class implementing IApplicantsContainer # or derivations thereof. This utility would be registered on startup and could then be used like this: >>> from zope.component import getAllUtilitiesRegisteredFor >>> from waeup.sirp.applicants.interfaces import ( ... IApplicantsContainerProvider) >>> all_providers = getAllUtilitiesRegisteredFor( ... IApplicantsContainerProvider) >>> all_providers [] You could look up this specific provider by name: >>> from zope.component import getUtility >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider') >>> p An applicants container would then be created like this: >>> provider = all_providers[0] >>> container = provider.factory() >>> container """ factory = Attribute("A class that can create instances of the " "requested container type")