Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 15872)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 15873)
@@ -4,4 +4,6 @@
 1.6.1.dev0 (unreleased)
 =======================
+
+* Add `StudentOutstandingCoursesExporter`.
 
 * Sort lecturers on scores slip.
Index: main/waeup.kofa/trunk/docs/source/userdocs/datacenter/export.rst
===================================================================
--- main/waeup.kofa/trunk/docs/source/userdocs/datacenter/export.rst	(revision 15872)
+++ main/waeup.kofa/trunk/docs/source/userdocs/datacenter/export.rst	(revision 15873)
@@ -221,4 +221,13 @@
   .. automethod:: waeup.kofa.students.export.CourseTicketExporter.mangle_value()
 
+Student Outstanding Courses Exporter
+----------------------
+
+.. autoclass:: waeup.kofa.students.export.StudentOutstandingCoursesExporter()
+
+  .. autoattribute:: waeup.kofa.students.export.StudentOutstandingCoursesExporter.fields
+  .. autoattribute:: waeup.kofa.students.export.StudentOutstandingCoursesExporter.title
+  .. automethod:: waeup.kofa.students.export.StudentOutstandingCoursesExporter.mangle_value()
+
 Student Payment Exporter
 ------------------------
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/export.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 15872)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 15873)
@@ -25,4 +25,5 @@
     IExtFileStore, IFileStoreNameChooser, IKofaUtils)
 from waeup.kofa.interfaces import MessageFactory as _
+from waeup.kofa.university.interfaces import ICertificateCourse, ICourse
 from waeup.kofa.students.catalog import StudentsQuery, CourseTicketsQuery
 from waeup.kofa.students.interfaces import (
@@ -133,4 +134,29 @@
     return tickets
 
+def get_outstanding(students, **kw):
+    """Get students with outstanding certificate courses.
+    """
+    students_wo = []
+    for student in students:
+        certificate = getattr(
+            student.get('studycourse', None), 'certificate', None)
+        if certificate:
+            allticketcodes = []
+            failedticketcodes = '' # taken but failed
+            nottakenticketcodes = '' # registered but not taken
+            missedticketcodes = '' # not registered
+            for level in student['studycourse'].values():
+                failedticketcodes += level.passed_params[4]
+                nottakenticketcodes += level.passed_params[5]
+                for ticket in level.values():
+                    allticketcodes.append(ticket.code)
+            for certcourse in certificate.values():
+                if certcourse.getCourseCode() not in allticketcodes:
+                    missedticketcodes += '%s ' % certcourse.__name__
+            student_wo = (student, missedticketcodes,
+                          failedticketcodes, nottakenticketcodes)
+            students_wo.append(student_wo)
+    return students_wo
+
 def get_payments(students, p_states=None, **kw):
     """Get all payments of `students` within given payment_date period.
@@ -430,4 +456,37 @@
             value, name, context=context)
 
+class StudentOutstandingCoursesExporter(grok.GlobalUtility, StudentExporterBase):
+    """The Student Outstanding Courses Exporter first filters the set of 
+    students by searching the students catalog. Then it exports students with 
+    lists of outstanding courses, i.e. courses which the student has
+    missed (not registered at all), failed (registered but not passed)
+    or nottaken (registered but not taken).
+    """
+    grok.name('studentoutstandingcourses')
+
+    fields = ('student_id', 'certcode', 'display_fullname','missed',
+              'failed', 'nottaken')
+    title = _(u'Student Outstanding Courses')
+
+    def filter_func(self, x, **kw):
+        return get_outstanding(x, **kw)
+
+    def mangle_value(self, value, name, context=None):
+        """The mangler determines the student's id, fullname and certcode,
+        and it collects the lists of outstanding courses.
+        """
+        if context is not None:
+            if name in ('student_id', 'display_fullname', 'certcode'):
+                value = getattr(context[0], name, None)
+            elif name == 'missed':
+                value = context[1]
+            elif name == 'failed':
+                value = context[2]
+            elif name == 'nottaken':
+                value = context[3]
+        return super(
+            StudentOutstandingCoursesExporter, self).mangle_value(
+            value, name, context=context)
+
 class StudentPaymentExporter(grok.GlobalUtility, StudentExporterBase):
     """The Student Payment Exporter first filters the set of students
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 15872)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 15873)
@@ -27,4 +27,6 @@
     ICSVExporter, IExtFileStore, IFileStoreNameChooser)
 from waeup.kofa.students.catalog import StudentsQuery
+from waeup.kofa.university.course import Course
+from waeup.kofa.university.certificate import CertificateCourse
 from waeup.kofa.students.export import (
     StudentExporter, StudentStudyCourseExporter, StudentStudyLevelExporter,
@@ -33,4 +35,5 @@
     ComboCardDataExporter, DataForBursaryExporter,
     StudentUnpaidPaymentExporter, SessionPaymentsOverviewExporter,
+    StudentOutstandingCoursesExporter,
     AccommodationPaymentsExporter, get_students,)
 from waeup.kofa.students.accommodation import BedTicket
@@ -789,4 +792,51 @@
             '1,1,CRS1,,100,DEP1,FAC1,110,2012,0,0,100,,2,,Course 1,A111111,CERT1,'
             'Anna M. Tester\r\n'
+            )
+        return
+
+class StudentOutstandingCoursesExporterTest(StudentImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(StudentOutstandingCoursesExporterTest, self).setUp()
+        self.setup_for_export()
+        return
+
+    def test_ifaces(self):
+        # make sure we fullfill interface contracts
+        obj = StudentOutstandingCoursesExporter()
+        verifyObject(ICSVStudentExporter, obj)
+        verifyClass(ICSVStudentExporter, StudentOutstandingCoursesExporter)
+        return
+
+    def test_get_as_utility(self):
+        # we can get an student exporter as utility
+        result = queryUtility(ICSVExporter, name="studentoutstandingcourses")
+        self.assertTrue(result is not None)
+        return
+
+    def test_export_all(self):
+        course1 = Course(u'Cheese Basics', 'C1')
+        course2 = Course(u'Advanced Cheese Making', 'C2')
+        course3 = Course(u'Selling Cheese', 'C3')
+        self.app['faculties']['fac1']['dep1'].courses.addCourse(course1)
+        self.app['faculties']['fac1']['dep1'].courses.addCourse(course2)
+        self.app['faculties']['fac1']['dep1'].courses.addCourse(course3)
+        self.certificate.addCertCourse(course1, 100, True)
+        self.certificate.addCertCourse(course2, 400, False)
+        self.certificate.addCertCourse(course3, 100, False)
+        self.setup_student(self.student)
+        self.student['studycourse']['100']['C3'].score = 25
+        exporter = StudentOutstandingCoursesExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        # The only student has registered C1, C3 and CRS1
+        # She missed C2, has failed C3 and  did not
+        # take C1 and CRS1
+        self.assertEqual(
+            result,
+            'student_id,certcode,display_fullname,missed,failed,nottaken\r\n'
+            'A111111,CERT1,Anna M. Tester,C2_400 ,C3 ,C1 CRS1 \r\n'
             )
         return
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 15872)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/utils.py	(revision 15873)
@@ -1142,9 +1142,10 @@
     #: subobjects thereof.
     STUDENT_EXPORTER_NAMES = ('students', 'studentstudycourses',
-            'studentstudylevels', 'coursetickets',
+            'studentstudylevels', 'coursetickets','studentoutstandingcourses',
             'studentpayments', 'studentunpaidpayments',
-            'bedtickets', 'sfpaymentsoverview', 'sessionpaymentsoverview',
-            'studylevelsoverview', 'combocard', 'bursary',
-            'accommodationpayments')
+            'sfpaymentsoverview', 'sessionpaymentsoverview',
+            'studylevelsoverview','bedtickets',
+            'combocard', 'bursary',
+            'accommodationpayments',)
 
     #: A tuple containing all exporter names needed for backing
