Index: main/waeup.sirp/trunk/src/waeup/sirp/students/tests/sample_courseticket_data.csv
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/tests/sample_courseticket_data.csv	(revision 7548)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/tests/sample_courseticket_data.csv	(revision 7548)
@@ -0,0 +1,6 @@
+reg_number,matric_number,level,code,score,core_or_elective
+1,,100,COURSE1,1,True
+2,,100,COURSE1,2,False
+,100002,100,COURSE1,3,False
+1,,nonsense,COURSE1,5,
+1,,100,NONSENSE,5,
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_batching.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_batching.py	(revision 7539)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_batching.py	(revision 7548)
@@ -32,5 +32,5 @@
 from waeup.sirp.students.batching import (
     StudentProcessor, StudentStudyCourseProcessor,
-    StudentStudyLevelProcessor)
+    StudentStudyLevelProcessor, CourseTicketProcessor)
 from waeup.sirp.students.student import Student
 from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
@@ -77,4 +77,11 @@
 
 STUDYLEVEL_HEADER_FIELDS = STUDYLEVEL_SAMPLE_DATA.split(
+    '\n')[0].split(',')
+
+COURSETICKET_SAMPLE_DATA = open(
+    os.path.join(os.path.dirname(__file__), 'sample_courseticket_data.csv'),
+    'rb').read()
+
+COURSETICKET_HEADER_FIELDS = COURSETICKET_SAMPLE_DATA.split(
     '\n')[0].split(',')
 
@@ -161,4 +168,17 @@
         assert len(self.app['students'].keys()) == 2
 
+    def test_checkConversion(self):
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', reg_state='admitted'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', reg_state=''))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('reg_state', 'no value provided') in errs)
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', reg_state='nonsense'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('reg_state', 'not allowed') in errs)
+
     def test_delEntry(self):
         assert self.student.student_id in self.app['students'].keys()
@@ -283,4 +303,21 @@
         self.assertEqual(student.reg_number,'1')
 
+    def test_checkConversion(self):
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', certificate='CERT1', current_level='200'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', certificate='CERT999'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('certificate', u'Invalid value') in errs)
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', certificate='CERT1', current_level='100'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('current_level','not in range') in errs)
+        # If we import only current_level, no conversion checking is done.
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', current_level='100'))
+        self.assertEqual(len(errs),0)
+
     def test_import(self):
         num, num_warns, fin_file, fail_file = self.importer.doImport(
@@ -350,4 +387,17 @@
             IBatchProcessor, StudentStudyLevelProcessor) is True
 
+    def test_checkConversion(self):
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', level='220'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', level='900'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('level','no valid integer') in errs)
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', level='xyz'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('level','no integer') in errs)
+
     def test_import(self):
         num, num_warns, fin_file, fail_file = self.importer.doImport(
@@ -365,9 +415,119 @@
         
 
+class CourseTicketImporterTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CourseTicketImporterTest, self).setUp()
+        self.dc_root = tempfile.mkdtemp()
+        self.workdir = tempfile.mkdtemp()
+        app = University()
+        app['datacenter'].setStoragePath(self.dc_root)
+        self.getRootFolder()['app'] = app
+        self.app = self.getRootFolder()['app']
+        setSite(app)
+
+        # Import students with subobjects
+        student_file = os.path.join(self.workdir, 'sample_student_data.csv')
+        open(student_file, 'wb').write(STUDENT_SAMPLE_DATA)
+        num, num_warns, fin_file, fail_file = StudentProcessor().doImport(
+            student_file, STUDENT_HEADER_FIELDS)
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        # Populate university
+        self.certificate = createObject('waeup.Certificate')
+        self.certificate.code = 'CERT1'
+        self.certificate.application_category = 'basic'
+        self.certificate.start_level = 200
+        self.certificate.end_level = 500
+        self.app['faculties']['fac1'] = Faculty()
+        self.app['faculties']['fac1']['dep1'] = Department()
+        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
+            self.certificate)
+        self.course = createObject('waeup.Course')
+        self.course.code = 'COURSE1'
+        self.course.semester = 1
+        self.course.credits = 10
+        self.course.passmark = 40
+        self.app['faculties']['fac1']['dep1'].courses.addCourse(
+            self.course)
+        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCourseRef(
+            self.course, level=100)
+
+        # Update study courses
+        studycourse_file = os.path.join(
+            self.workdir, 'sample_studycourse_data.csv')
+        open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA)
+        importer = StudentStudyCourseProcessor()
+        num, num_warns, fin_file, fail_file = importer.doImport(
+            studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update')
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        # Import study levels
+        importer = StudentStudyLevelProcessor()
+        studylevel_file = os.path.join(
+            self.workdir, 'sample_studylevel_data.csv')
+        open(studylevel_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA)
+        num, num_warns, fin_file, fail_file = importer.doImport(
+            studylevel_file, STUDYLEVEL_HEADER_FIELDS,'create')
+        shutil.rmtree(os.path.dirname(fin_file))
+
+        self.importer = CourseTicketProcessor()
+        self.csv_file = os.path.join(
+            self.workdir, 'sample_courseticket_data.csv')
+        open(self.csv_file, 'wb').write(COURSETICKET_SAMPLE_DATA)
+
+    def tearDown(self):
+        super(CourseTicketImporterTest, self).tearDown()
+        shutil.rmtree(self.workdir)
+        shutil.rmtree(self.dc_root)
+        clearSite()
+        return
+
+    def test_interface(self):
+        # Make sure we fulfill the interface contracts.
+        assert verifyObject(IBatchProcessor, self.importer) is True
+        assert verifyClass(
+            IBatchProcessor, CourseTicketProcessor) is True
+
+    def test_checkConversion(self):
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', code='COURSE1', level='220'))
+        self.assertEqual(len(errs),0)
+        errs, inv_errs, conv_dict = self.importer.checkConversion(
+            dict(reg_number='1', code='COURSE2', level='220'))
+        self.assertEqual(len(errs),1)
+        self.assertTrue(('code','non-existent') in errs)
+
+    def test_import(self):
+
+        num, num_warns, fin_file, fail_file = self.importer.doImport(
+            self.csv_file, COURSETICKET_HEADER_FIELDS,'create')
+
+        self.assertEqual(num_warns,2)
+        assert self.importer.entryExists(
+            dict(reg_number='1', level='100', code='COURSE1'), self.app) is True
+        courseticket = self.importer.getEntry(
+            dict(reg_number='1', level='100', code='COURSE1'), self.app)
+        self.assertEqual(courseticket.__parent__.__parent__.certificate.code, u'CERT1')
+        self.assertEqual(courseticket.score, 1)
+        self.assertEqual(courseticket.core_or_elective, True)
+        self.assertEqual(courseticket.fcode, 'NA')
+        self.assertEqual(courseticket.dcode, 'NA')
+        self.assertEqual(courseticket.code, 'COURSE1')
+        self.assertEqual(courseticket.title, 'Unnamed Course')
+        self.assertEqual(courseticket.credits, 10)
+        self.assertEqual(courseticket.passmark, 40)
+        self.assertEqual(courseticket.semester, 1)
+        #import pdb; pdb.set_trace()
+        shutil.rmtree(os.path.dirname(fin_file))
+
+
 def test_suite():
     suite = unittest.TestSuite()
     for testcase in [
         StudentImporterTest,StudentStudyCourseImporterTest,
-        StudentStudyLevelImporterTest,]:
+        StudentStudyLevelImporterTest,CourseTicketImporterTest,]:
         suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
                 testcase
