Index: /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/batching.py
===================================================================
--- /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/batching.py	(revision 9465)
+++ /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/batching.py	(revision 9465)
@@ -0,0 +1,38 @@
+## $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
+##
+"""Batch processing for applicants.
+"""
+from waeup.kofa.utils.batching import BatchProcessor
+from waeup.kofa.applicants.batching import ApplicantProcessor
+from waeup.fceokene.applicants.interfaces import (
+    ICustomApplicant, ICustomApplicantUpdateByRegNo)
+
+class CustomApplicantProcessor(ApplicantProcessor):
+    """A batch processor for IApplicant objects.
+
+    In create mode container_code is required. If application_number is given
+    an applicant with this number is created in the designated container.
+    If application_number is not given a random application_number is assigned.
+    applicant_id is being determined by the system and can't be imported.
+
+    In update or remove mode container_code and application_number columns
+    must not exist. The applicant object is solely searched by its applicant_id
+    or reg_number.
+    """
+    iface = ICustomApplicant
+    iface_byregnumber = ICustomApplicantUpdateByRegNo
Index: /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/export.py
===================================================================
--- /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/export.py	(revision 9465)
+++ /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/export.py	(revision 9465)
@@ -0,0 +1,34 @@
+## $Id$
+##
+## Copyright (C) 2012 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
+##
+"""Exporters for applicant-related stuff.
+"""
+import grok
+from waeup.kofa.applicants.interfaces import IApplicantBaseData
+from waeup.kofa.applicants.export import ApplicantsExporter
+from kofacustom.nigeria.applicants.interfaces import INigeriaUGApplicant
+from waeup.fceokene.applicants.interfaces import ICustomUGApplicant
+
+class CustomApplicantsExporter(ApplicantsExporter):
+    """Exporter for Nigeria Applicants.
+    """
+
+    fields = tuple(sorted(set(
+        ICustomUGApplicant.names() +
+        INigeriaUGApplicant.names() +
+        IApplicantBaseData.names()
+        ))) + ('container_code',)
Index: /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/interfaces.py
===================================================================
--- /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/interfaces.py	(revision 9464)
+++ /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/interfaces.py	(revision 9465)
@@ -27,4 +27,5 @@
     SimpleKofaVocabulary, academic_sessions_vocab, validate_email)
 from waeup.kofa.schema import FormattedDate, TextLineChoice
+from waeup.kofa.schoolgrades import ResultEntryField
 from waeup.kofa.students.vocabularies import nats_vocab, GenderSource
 from waeup.kofa.applicants.interfaces import contextual_reg_num_source
@@ -60,20 +61,33 @@
     """
 
+    nationality = schema.Choice(
+        source = nats_vocab,
+        title = _(u'Nationality'),
+        required = True,
+        )
+    lga = schema.Choice(
+        source = LGASource(),
+        title = _(u'State/LGA (Nigerians only)'),
+        required = False,
+        )
+    #perm_address = schema.Text(
+    #    title = _(u'Permanent Address'),
+    #    required = False,
+    #    )
+    course1 = schema.Choice(
+        title = _(u'1st Choice Course of Study'),
+        source = AppCatCertificateSource(),
+        required = True,
+        )
+    course2 = schema.Choice(
+        title = _(u'2nd Choice Course of Study'),
+        source = AppCatCertificateSource(),
+        required = False,
+        )
     olevel_type = schema.Choice(
         title = _(u'Qualification Obtained'),
         required = False,
         readonly = False,
-        vocabulary = high_qual,
-        )
-    olevel_matric_no = schema.TextLine(
-        title = _(u'Former Matric Number'),
-        required = False,
-        readonly = False,
-        )
-    olevel_degree = schema.Choice(
-        title = _(u'Class of Degree'),
-        required = False,
-        readonly = False,
-        vocabulary = high_grade,
+        vocabulary = exam_types,
         )
     olevel_school = schema.TextLine(
@@ -82,13 +96,100 @@
         readonly = False,
         )
-    olevel_session = schema.TextLine(
+    olevel_exam_number = schema.TextLine(
+        title = _(u'Exam Number'),
+        required = False,
+        readonly = False,
+        )
+    olevel_exam_date = FormattedDate(
+        title = _(u'Exam Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+    olevel_results = schema.List(
+        title = _(u'Exam Results'),
+        value_type = ResultEntryField(),
+        required = False,
+        readonly = False,
+        default = [],
+        )
+    hq_type = schema.Choice(
+        title = _(u'Qualification Obtained'),
+        required = False,
+        readonly = False,
+        vocabulary = high_qual,
+        )
+    hq_matric_no = schema.TextLine(
+        title = _(u'Former Matric Number'),
+        required = False,
+        readonly = False,
+        )
+    hq_degree = schema.Choice(
+        title = _(u'Class of Degree'),
+        required = False,
+        readonly = False,
+        vocabulary = high_grade,
+        )
+    hq_school = schema.TextLine(
+        title = _(u'Institution Attended'),
+        required = False,
+        readonly = False,
+        )
+    hq_session = schema.TextLine(
         title = _(u'Years Attended'),
         required = False,
         readonly = False,
         )
-    olevel_disc = schema.TextLine(
+    hq_disc = schema.TextLine(
         title = _(u'Discipline'),
         required = False,
         readonly = False,
+        )
+    jamb_subjects = schema.Text(
+        title = _(u'Subjects and Scores'),
+        required = False,
+        )
+    jamb_score = schema.Int(
+        title = _(u'Total JAMB Score'),
+        required = False,
+        )
+    notice = schema.Text(
+        title = _(u'Notice'),
+        required = False,
+        )
+    screening_venue = schema.TextLine(
+        title = _(u'Screening Venue'),
+        required = False,
+        )
+    screening_date = schema.TextLine(
+        title = _(u'Screening Date'),
+        required = False,
+        )
+    screening_score = schema.Int(
+        title = _(u'Screening Score (%)'),
+        required = False,
+        )
+    aggregate = schema.Int(
+        title = _(u'Aggregate Score (%)'),
+        description = _(u'(average of relative JAMB and PUTME scores)'),
+        required = False,
+        )
+    result_uploaded = schema.Bool(
+        title = _(u'Result uploaded'),
+        default = False,
+        )
+    student_id = schema.TextLine(
+        title = _(u'Student Id'),
+        required = False,
+        readonly = False,
+        )
+    course_admitted = schema.Choice(
+        title = _(u'Admitted Course of Study'),
+        source = CertificateSource(),
+        required = False,
+        )
+    locked = schema.Bool(
+        title = _(u'Form locked'),
+        default = False,
         )
 
Index: /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/tests/test_browser.py
===================================================================
--- /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/tests/test_browser.py	(revision 9465)
+++ /main/waeup.fceokene/trunk/src/waeup/fceokene/applicants/tests/test_browser.py	(revision 9465)
@@ -0,0 +1,124 @@
+## $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
+##
+import shutil
+import tempfile
+import datetime
+import pytz
+from zope.component.hooks import setSite, clearSite
+from zope.component import createObject
+from zope.testbrowser.testing import Browser
+from waeup.kofa.app import University
+from waeup.kofa.university.faculty import Faculty
+from waeup.kofa.university.department import Department
+from waeup.kofa.testing import FunctionalTestCase
+from waeup.kofa.applicants.container import ApplicantsContainer
+from waeup.fceokene.testing import FunctionalLayer
+
+
+class ApplicantUITest(FunctionalTestCase):
+    """Perform some browser tests.
+    """
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(ApplicantUITest, self).setUp()
+        # Setup a sample site for each test
+        app = University()
+        self.dc_root = tempfile.mkdtemp()
+        app['datacenter'].setStoragePath(self.dc_root)
+
+        # Prepopulate the ZODB...
+        self.getRootFolder()['app'] = app
+        # we add the site immediately after creation to the
+        # ZODB. Catalogs and other local utilities are not setup
+        # before that step.
+        self.app = self.getRootFolder()['app']
+        # Set site here. Some of the following setup code might need
+        # to access grok.getSite() and should get our new app then
+        setSite(app)
+
+        # Add bec applicants container
+        self.beccontainer = ApplicantsContainer()
+        self.beccontainer.mode = 'create'
+        self.beccontainer.code = u'bec2011'
+        self.beccontainer.prefix = u'bec'
+        self.beccontainer.application_category = u'bec'
+        self.beccontainer.year = 2011
+        self.beccontainer.application_fee = 300.0
+        self.beccontainer.title = u'This is the bec2011 container'
+        self.app['applicants']['bec2011'] = self.beccontainer
+
+        delta = datetime.timedelta(days=10)
+        self.beccontainer.startdate = datetime.datetime.now(pytz.utc) - delta
+        self.beccontainer.enddate = datetime.datetime.now(pytz.utc) + delta
+
+        # Populate university
+        self.certificate = createObject('waeup.Certificate')
+        self.certificate.code = 'CERT1'
+        self.certificate.application_category = 'bec'
+        self.certificate.start_level = 100
+        self.certificate.end_level = 500
+        self.certificate.study_mode = u'ug_ft'
+        self.app['faculties']['fac1'] = Faculty()
+        self.app['faculties']['fac1']['dep1'] = Department()
+        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
+            self.certificate)
+
+        # Add (customized) applicants
+        becapplicant = createObject(u'waeup.Applicant')
+        becapplicant.firstname = u'Anna'
+        becapplicant.lastname = u'Post'
+        self.app['applicants']['bec2011'].addApplicant(becapplicant)
+        self.becapplication_number = becapplicant.application_number
+        self.becapplicant = self.app['applicants']['bec2011'][
+            self.becapplication_number]
+        self.becapplicant_path = ('http://localhost/app/applicants/bec2011/%s'
+            % self.becapplication_number)
+
+        self.browser = Browser()
+        self.browser.handleErrors = False
+
+    def tearDown(self):
+        super(ApplicantUITest, self).tearDown()
+        shutil.rmtree(self.dc_root)
+        clearSite()
+        return
+
+    def fill_correct_values(self):
+        self.browser.getControl(name="form.reg_number").value = '1234'
+        self.browser.getControl(name="form.firstname").value = 'John'
+        self.browser.getControl(name="form.lastname").value = 'Tester'
+        self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'
+        self.browser.getControl(name="form.lga").value = ['foreigner']
+        self.browser.getControl(name="form.nationality").value = ['NG']
+        self.browser.getControl(name="form.sex").value = ['m']
+        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
+
+    def test_manage_and_view_applicant(self):
+        # Managers can manage bec applicants.
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.becapplicant_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertTrue("'O' Level" in self.browser.contents)
+        self.browser.open(self.becapplicant_path + '/manage')
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.browser.open(self.becapplicant_path + '/edit')
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.browser.open(self.becapplicant_path + '/application_slip.pdf')
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        return
