## ## interfaces.py ## Login : ## Started on Sun Jan 16 15:30:01 2011 Uli Fouquet ## $Id$ ## ## Copyright (C) 2011 Uli Fouquet ## 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 hurry.file import HurryFile from zc.sourcefactory.basic import BasicSourceFactory from zope import schema 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.interfaces import IWAeUPObject, SimpleWAeUPVocabulary IMAGE_PATH = os.path.join( os.path.dirname(waeup.sirp.browser.__file__), 'static' ) DEFAULT_PASSPORT_IMAGE_MALE = HurryFile( 'passport.jpg', open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg')).read(), ) DEFAULT_PASSPORT_IMAGE_FEMALE = HurryFile( 'passport.jpg', open(os.path.join(IMAGE_PATH, 'placeholder_f.jpg')).read(), ) #: Categories of applications we support. #: Yet not complete nor correct. APPLICATION_CATEGORIES = ( ('Direct Entry Screening Exam (PDE)', 'pde'), ('Post-UME', 'pume'), ('Post-UDE', 'pude'), ('PCE', 'pce'), ('Common Entry Screening Test (CEST)', 'cest'), ) #: A :class:`waeup.sirp.interfaces.SimpleWAeUPVocabulary` of supported #: application categories. application_categories_vocab = SimpleWAeUPVocabulary(*APPLICATION_CATEGORIES) 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 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): """A container for university applicants containers. """ def addApplicantsContainer(container, name=None): """Add an applicants container. Adds an applicants container that implements `interface` under the name `name`. `container` the container instance to be added. Should implement :class:`IApplicantsContainer`. `name` the name under which the container will be accessible. We usually use names like ``pume_2011`` to indicate, that the new container will contain university applicants for a certain screening type (``pume``) and of the year 2011. """ class IApplicantsContainer(IWAeUPObject): """An applicants container contains university applicants. """ REQUIRES_JAMBDATA = schema.Bool( title = u'JAMB data required', description = u'This container requires JAMB data to be available.', required = True, default = False, ) name = schema.TextLine( title = u'Internal ID', required = True, ) title = schema.TextLine( title = u'Short description of the type of applicants stored here.', required = True, default = u'Untitled', ) description = schema.Text( title = u'Human readable description in reST format', required = False, default = u'Not set.' ) 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 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. """ reg_no = schema.TextLine( title = u'JAMB Registration Number', ) access_code = schema.TextLine( title = u'Access Code', required = False, ) serial = schema.TextLine( title = u'Serial Number', required = False, ) course1 = schema.TextLine( title = u'1st Choice Course of Study', required = False, ) course2 = schema.TextLine( title = u'2nd Choice Course of Study', required = False, ) course3 = schema.TextLine( title = u'3rd 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'Surname/Full Name', required = False, ) jamb_age = schema.Int( title = u'Age (provided by JAMB)', required = False, ) date_of_birth = schema.Date( title = u'Date of Birth', required = False, ) jamb_state = schema.TextLine( title = u'State (provided by JAMB)', required = False, ) jamb_lga = schema.TextLine( title = u'LGA (provided by JAMB)', required = False, ) lga = schema.TextLine( # XXX: should be choice title = u'State/LGA (confirmed by applicant)', 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 = schema.Bool( # title = u'Passport Photograph', # default = True, # required = False, # ) passport = ImageFile( title = u'Passport Photograph', default = DEFAULT_PASSPORT_IMAGE_MALE, required = True, ) aos = schema.TextLine( # XXX: should be choice title = u'Area of Specialisation', required = False, ) subj1 = schema.TextLine( # XXX: should be choice title = u'1st Choice of Study', required = False, ) subj2 = schema.TextLine( # XXX: should be choice title = u'2nd Choice of Study', required = False, ) subj3 = schema.TextLine( # XXX: should be choice title = u'3rd Choice of Study', required = False, ) # # Higher Educational Data # hq_matric_no = schema.TextLine( title = u'Former Matric Number', required = False, ) hq_type = schema.TextLine( title = u'Higher Qualification', required = False, ) hq_grade = schema.TextLine( title = u'Higher Qualification Grade', required = False, ) hq_school = schema.TextLine( title = u'School Attended', required = False, ) hq_session = schema.TextLine( title = u'Session Obtained', required = False, ) hq_disc = schema.TextLine( title = u'Discipline', required = False, ) # # First sitting data # fst_sit_fname = schema.TextLine( title = u'Full Name', required = False, ) fst_sit_no = schema.TextLine( title = u'Exam Number', required = False, ) fst_sit_date = schema.Date( title = u'Exam Date (dd/mm/yyyy)', required = False, ) fst_sit_type = schema.TextLine( # XXX: Should be choice title = u'Exam Type', required = False, ) fst_sit_results = schema.List( title = u'Results', required = False, value_type = schema.Object( title = u'Entries', schema = IResultEntry, required = False, ) ) scd_sit_fname = schema.TextLine( title = u'Full Name', required = False, ) scd_sit_no = schema.TextLine( title = u'Exam Number', required = False, ) scd_sit_date = schema.Date( title = u'Exam Date (dd/mm/yyyy)', required = False, ) scd_sit_type = schema.TextLine( # XXX: Should be choice title = u'Exam Type', required = False, ) scd_sit_results = schema.TextLine( # XXX: Should be nested list of choices title = u'Results', required = False, ) # # JAMB scores # eng_score = schema.TextLine( title = u"'English' score", required = False, ) subj1score = schema.TextLine( title = u'1st Choice of Study Score', required = False, ) subj2score = schema.TextLine( title = u'2nd Choice of Study Score', required = False, ) subj3score = schema.TextLine( title = u'3rd Choice of Study Score', required = False, ) # XXX: Total score??? # # Application Data # application_date = schema.Date( title = u'Application Date', required = False, ) status = schema.TextLine( # XXX: should be 'status' type title = u'Application Status', required = False, ) screening_date = schema.Date( title = u'Screening Date', required = False, ) screening_type = schema.TextLine( # XXX: schould be choice title = u'Screening Type', required = False, ) screening_score = schema.TextLine( title = u'Screening Score', required = False, ) screening_venue = schema.TextLine( title = u'Screening Venue', required = False, ) total_score = schema.TextLine( title = u'Total Score', required = False, ) course_admitted = schema.TextLine( # XXX: should be choice title = u'Admitted Course of Study', required = False, ) department = schema.TextLine( # XXX: if we have a course, dept. is not necessary title = u'Department', required = False, ) faculty = schema.TextLine( # XXX: if we have a course, faculty is not necessary title = u'Faculty', required = False, ) entry_session = schema.TextLine( # XXX: should be choice, should have sensible default: upcoming session title = u'Entry Session', required = False, ) notice = schema.Text( title = u'Notice', required = False, ) student_id = schema.TextLine( title = u'Student ID', required = False, ) import_record_no = schema.TextLine( title = u'Import Record No.', required = False, ) imported_by = schema.TextLine( title = u'Imported By', required = False, ) import_date = schema.Datetime( title = u'Import Date', required = False, ) import_from = schema.TextLine( title = u'Import Source', required = False, ) class IApplicant(IApplicantBaseData): """An applicant. This is basically the applicant base data. Here we repeat the fields from base data only with the `required` attribute of required attributes set to True (which is the default). """ access_code = schema.TextLine( title = u'Access Code', ) course1 = schema.TextLine( title = u'1st Choice Course of Study', ) firstname = schema.TextLine( title = u'First Name', ) middlenames = schema.TextLine( title = u'Middle Names', ) lastname = schema.TextLine( title = u'Surname/Full Name', ) date_of_birth = schema.Date( title = u'Date of Birth', ) jamb_state = schema.TextLine( title = u'State (provided by JAMB)', ) jamb_lga = schema.TextLine( title = u'LGA (provided by JAMB)', ) lga = schema.TextLine( # XXX: should be choice title = u'State/LGA (confirmed by applicant)', ) sex = schema.Choice( title = u'Sex', source = GenderSource(), default = u'm', ) #passport = schema.Bool( # title = u'Passport Photograph', # default = True, # ) passport = ImageFile( title = u'Passport Photograph', default = DEFAULT_PASSPORT_IMAGE_MALE, required = True, ) # # Higher Educational Data # # # First sitting data # fst_sit_fname = schema.TextLine( title = u'Full Name', ) # # Second sitting data # scd_sit_fname = schema.TextLine( title = u'Full Name', ) # # JAMB scores # # # Application Data # application_date = schema.Date( title = u'Application Date', ) status = schema.TextLine( # XXX: should be 'status' type title = u'Application Status', ) screening_date = schema.Date( title = u'Screening Date', ) screening_type = schema.TextLine( # XXX: schould be choice title = u'Screening Type', ) screening_score = schema.TextLine( title = u'Screening Score', ) entry_session = schema.TextLine( # XXX: should be choice # XXX: should have sensible default: upcoming session title = u'Entry Session', ) import_record_no = schema.TextLine( title = u'Import Record No.', ) imported_by = schema.TextLine( title = u'Imported By', ) import_date = schema.Datetime( title = u'Import Date', ) import_from = schema.TextLine( title = u'Import Source', ) class IApplicantPDEEditData(IWAeUPObject): """The data set presented to PDE applicants. """ reg_no = schema.TextLine( title = u'JAMB Registration Number', readonly = True, ) access_code = schema.TextLine( title = u'Access Code', readonly = True, ) course1 = schema.TextLine( title = u'1st Choice Course of Study', readonly = True, ) course2 = schema.TextLine( title = u'2nd Choice Course of Study', required = False, ) course3 = schema.TextLine( title = u'3rd Choice Course of Study', required = False, ) lastname = schema.TextLine( title = u'Name', readonly = True, ) jamb_age = schema.Int( title = u'Age', readonly = True, ) date_of_birth = schema.Date( title = u'Date of Birth', required = True, ) jamb_state = schema.TextLine( title = u'State (provided by JAMB)', readonly = True, ) jamb_lga = schema.TextLine( title = u'LGA (provided by JAMB)', readonly = True, ) lga = schema.TextLine( # XXX: should be choice title = u'State/LGA (confirmed by applicant)', required = False, ) email = schema.TextLine( title = u'Email', required = False, ) phone = schema.TextLine( title = u'Phone', required = False, ) aos = schema.TextLine( # XXX: should be choice title = u'Area of Specialisation', required = False, ) subj1 = schema.TextLine( # XXX: should be choice title = u'1st Choice of Study', readonly = True, ) subj2 = schema.TextLine( # XXX: should be choice title = u'2nd Choice of Study', required = False, ) subj3 = schema.TextLine( # XXX: should be choice title = u'3rd Choice of Study', required = False, ) # # Application Data # application_date = schema.Date( title = u'Application Date', readonly = True, ) status = schema.TextLine( # XXX: should be 'status' type title = u'Application Status', readonly = True, ) screening_date = schema.Date( title = u'Screening Date', readonly = True, ) #passport = schema.Bool( # title = u'Passport Photograph', # default = True, # required = False, # ), #passport = schema.Bytes( # title = u'Passport Photograph', # required = True, # ) #passport = schema.Object( # title = u'Passport Photograph', # required = True, # schema = IImage) passport = ImageFile( title = u'Passport Photograph', default = DEFAULT_PASSPORT_IMAGE_MALE, required = True, ) class IApplicantPrincipalInfo(IPrincipalInfo): """Infos about principals that are applicants. """ reg_no = Attribute("The JAMB registration no. of the user") 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. """ reg_no = schema.TextLine( title = u'Registration number', description = u'The JAMB registration number', required = True, readonly = True) 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 IJAMBApplicantsFormChallenger(IApplicantsFormChallenger): """A challenger that uses a browser form to collect applicant credentials for applicants in JAMB process. JAMB-screened applicants have to provide an extra registration no. provided by JAMB. """ jamb_reg_no_field = schema.TextLine( title = u'JAMB registration no.', description = u'''Field of the login page which is looked up for the JAMB registration number.''', default = u'jamb_reg_no', ) 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 IJAMBApplicantSessionCredentials(IApplicantSessionCredentials): """Interface for storing and accessing JAMB applicant credentials in a session. """ def __init__(access_code, jamb_reg_no): """Create credentials for JAMB screened applicants.""" def getJAMBRegNo(): """Return the JAMB registration no."""