Index: main/waeup.kofa/trunk/src/waeup/kofa/students/export.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 7993)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 7994)
@@ -6,5 +6,5 @@
 from waeup.kofa.interfaces import ICSVExporter
 from waeup.kofa.interfaces import MessageFactory as _
-from waeup.kofa.students.interfaces import IStudent
+from waeup.kofa.students.interfaces import IStudent, IStudentStudyCourse
 from waeup.kofa.utils.batching import ExporterBase
 from waeup.kofa.utils.helpers import iface_names
@@ -56,2 +56,53 @@
             student_id=(None, None))
         return self.export(students, filepath)
+
+class StudentStudyCourseExporter(grok.GlobalUtility, ExporterBase):
+    """Exporter for StudentStudyCourses.
+    """
+    grok.implements(ICSVExporter)
+    grok.name('studentstudycourses')
+
+    #: Fieldnames considered by this exporter
+    fields = tuple(sorted(iface_names(IStudentStudyCourse)))
+
+    #: The title under which this exporter will be displayed
+    title = _(u'Student Study Courses')
+
+    def mangle_value(self, value, name, context=None):
+        """Add the hash symbol at the end of date_of_birth.
+
+        This is to avoid annoying automatic date transformation
+        by Excel or Calc.
+        """
+        if name == 'certificate' and value is not None:
+            # XXX: hopefully cert codes are unique site-wide
+            value = value.code
+        return super(
+            StudentStudyCourseExporter, self).mangle_value(
+            value, name, context=context)
+
+
+    def export(self, study_courses, filepath=None):
+        """Export `study_courses`, an iterable, as CSV file.
+
+        If `filepath` is ``None``, a raw string with CSV data is returned.
+        """
+        writer, outfile = self.get_csv_writer(filepath)
+        for study_course in study_courses:
+            self.write_item(study_course, writer)
+        return self.close_outfile(filepath, outfile)
+
+    def export_all(self, site, filepath=None):
+        """Export study courses into filepath as CSV data.
+
+        If `filepath` is ``None``, a raw string with CSV data is returned.
+        """
+        catalog = queryUtility(
+            ICatalog, context=site, name='students_catalog', default=None)
+        if catalog is None:
+            return self.export([], filepath)
+        students = catalog.searchResults(
+            student_id=(None, None))
+        study_courses = [x.get('studycourse', None) for x in students
+                         if x is not None]
+        return self.export(study_courses, filepath)
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 7993)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 7994)
@@ -6,6 +6,7 @@
 from zope.intid.interfaces import IIntIds
 from waeup.kofa.interfaces import ICSVExporter
-from waeup.kofa.students.export import StudentsExporter
-from waeup.kofa.students.student import Student
+from waeup.kofa.students.export import (
+    StudentsExporter, StudentStudyCourseExporter)
+from waeup.kofa.students.student import Student, StudentStudyCourse
 from waeup.kofa.students.tests.test_batching import StudentImportExportSetup
 from waeup.kofa.testing import FunctionalLayer
@@ -95,2 +96,103 @@
         return
 
+class StudentStudyCourseExporterTest(StudentImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(StudentStudyCourseExporterTest, self).setUp()
+        student = Student()
+        student.student_id = u'A111111'
+        self.app['students'][student.student_id] = self.student = student
+        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
+        self.cat = getUtility(ICatalog, name='students_catalog')
+        self.intids = getUtility(IIntIds)
+        #self.study_course = StudyCourse()
+        return
+
+    def test_ifaces(self):
+        # make sure we fullfill interface contracts
+        obj = StudentStudyCourseExporter()
+        verifyObject(ICSVExporter, obj)
+        verifyClass(ICSVExporter, StudentStudyCourseExporter)
+        return
+
+    def test_get_as_utility(self):
+        # we can get an student exporter as utility
+        result = queryUtility(ICSVExporter, name="studentstudycourses")
+        self.assertTrue(result is not None)
+        return
+
+    def setup_student(self, student):
+        # set predictable values for `student`
+        student.matric_number = u'M123456'
+        student.adm_code = u'my adm code'
+        student.clearance_locked = False
+        student.clr_code = u'my clr code'
+        student.perm_address = u'Studentroad 21\nLagos 123456\n'
+        student.reg_number = u'123456'
+        student.student_id = u'A111111'
+        student.firstname = u'Anna'
+        student.lastname = u'Tester'
+        student.middlename = u'M.'
+        student.date_of_birth = datetime.date(1981, 2, 4)
+        student.sex = 'f'
+        student.email = 'anna@sample.com'
+        student.phone = u'+234-123-12345'
+        student.notice = u'Some notice\nin lines.'
+
+        study_course = student.get('studycourse')
+        student['studycourse'].certificate = self.certificate
+        student['studycourse'].entry_mode = 'ug_ft'
+        student['studycourse'].entry_session = 2010
+        student['studycourse'].current_session = 2012
+        student['studycourse'].current_level = int(self.certificate.start_level)
+        return student
+
+    def test_export_empty(self):
+        # we can export a nearly empty study course
+        study_course = StudentStudyCourse()
+        exporter = StudentStudyCourseExporter()
+        exporter.export([study_course], self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(
+            result,
+            'certificate,current_level,current_session,current_verdict,'
+            'entry_mode,entry_session,previous_verdict\r\n'
+
+            ',,,0,,,0\r\n'
+            )
+        return
+
+    def test_export(self):
+        # we can really export study courses.
+        # set values we can expect in export file
+        self.setup_student(self.student)
+        study_course = self.student.get('studycourse')
+        exporter = StudentStudyCourseExporter()
+        exporter.export([study_course], self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(
+            result,
+            'certificate,current_level,current_session,current_verdict,'
+            'entry_mode,entry_session,previous_verdict\r\n'
+
+            'CERT1,200,2012,0,ug_ft,2010,0\r\n'
+            )
+        return
+
+    def test_export_all(self):
+        # we can really export students
+        # set values we can expect in export file
+        self.setup_student(self.student)
+        exporter = StudentStudyCourseExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(
+            result,
+            'certificate,current_level,current_session,current_verdict,'
+            'entry_mode,entry_session,previous_verdict\r\n'
+
+            'CERT1,200,2012,0,ug_ft,2010,0\r\n'
+            )
+        return
