Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 14983)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 14984)
@@ -4,4 +4,6 @@
 1.6.dev0 (unreleased)
 =======================
+
+* Add ticket level and session filter to `CourseTicketExporter`.
 
 * Add 'Student Record Creation' Report page.
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/exportconfig.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/exportconfig.pt	(revision 14983)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/exportconfig.pt	(revision 14984)
@@ -50,6 +50,7 @@
     </option>
   </select>
-  <br />
+
   <span id="payment_dates" style="display: none;">
+    <br />
     <label for="mode" i18n:translate="">
       Payment Date
@@ -65,5 +66,31 @@
     <br />
   </span>
+
+
+  <span id="ct_data" style="display: none;">
+    <br />
+    <label for="mode" i18n:translate="">
+      Course Ticket Level
+    </label>
+    <select name="ct_level" class="form-control half">
+      <option tal:repeat="item view/levels"
+              tal:attributes="value python:item[1]">
+        <span tal:replace="python: item[0]">200</span>
+      </option>
+    </select>
+    <br />
+    <label for="mode" i18n:translate="">
+      Course Ticket Session
+    </label>
+    <select name="ct_session" class="form-control half">
+      <option tal:repeat="item view/sessions"
+              tal:attributes="value python:item[1]">
+        <span tal:replace="python: item[0]">2012/2013</span>
+      </option>
+    </select>
+    <br />
+  </span>
   <br />
+
   <input type="submit" name="START"  i18n:translate=""
     value="Create CSV file"
@@ -77,7 +104,13 @@
           document.getElementById('exporter').value == 'studentunpaidpayments') {
           document.getElementById('payment_dates').style.display = 'block';
+          document.getElementById('ct_data').style.display = 'none';
+      } else if (document.getElementById('exporter').value == 'coursetickets')  {
+          document.getElementById('ct_data').style.display = 'block';
+          document.getElementById('payment_dates').style.display = 'none';
       } else {
+          document.getElementById('ct_data').style.display = 'none';
           document.getElementById('payment_dates').style.display = 'none';
       }
   }
 </script>
+
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/export.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 14983)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/export.py	(revision 14984)
@@ -57,14 +57,29 @@
     """Get course tickets of `students`.
     If code is passed through, filter course tickets
-    which belong to this course code and meets level
-    and level_session.
+    which belong to this course code and meet level=level
+    and level_session=level_session.
+    If not, but ct_level and ct_session
+    are passed through, filter course tickets
+    which meet level==ct_level and level_session==ct_session.
     """
     tickets = []
     code = kw.get('code', None)
     level = kw.get('level', None)
-    level_session = kw.get('session', None)
+    session = kw.get('session', None)
+    ct_level = kw.get('ct_level', None)
+    ct_session = kw.get('ct_session', None)
     if code is None:
         for level_obj in get_levels(students):
             for ticket in level_obj.values():
+                if ct_level not in ('all', None):
+                    if level_obj.level in (10, 999, None)  \
+                        and int(ct_level) != level_obj.level:
+                        continue
+                    if level_obj.level not in range(
+                        int(ct_level), int(ct_level)+100, 10):
+                        continue
+                if ct_session not in ('all', None) and \
+                    int(ct_session) != level_obj.level_session:
+                    continue
                 tickets.append(ticket)
     else:
@@ -73,13 +88,13 @@
                 if ticket.code != code:
                     continue
-                if level is not None:
-                    level = int(level)
+                if level not in ('all', None):
                     if level_obj.level in (10, 999, None)  \
                         and int(level) != level_obj.level:
                         continue
-                    if level_obj.level not in range(level, level+100, 10):
+                    if level_obj.level not in range(
+                        int(level), int(level)+100, 10):
                         continue
-                if level_session is not None and \
-                    int(level_session) != level_obj.level_session:
+                if session not in ('all', None) and \
+                    int(session) != level_obj.level_session:
                     continue
                 tickets.append(ticket)
@@ -101,12 +116,11 @@
             if not ticket.editable_by_lecturer:
                 continue
-            if level is not None:
-                level = int(level)
+            if level not in ('all', None):
                 if level_obj.level in (10, 999, None)  \
                     and int(level) != level_obj.level:
                     continue
-                if level_obj.level not in range(level, level+100, 10):
+                if level_obj.level not in range(int(level), int(level)+100, 10):
                     continue
-            if level_session is not None and \
+            if level_session not in ('all', None) and \
                 int(level_session) != level_obj.level_session:
                 continue
@@ -211,4 +225,9 @@
         except KeyError:
             pass
+        try:
+            del kw['ct_level']
+            del kw['ct_session']
+        except KeyError:
+            pass
         query = StudentsQuery(**kw)
         return query.query()
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 14983)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py	(revision 14984)
@@ -680,6 +680,5 @@
 
         exporter = CourseTicketExporter()
-        exporter.export_filtered(
-            self.student, self.outfile)
+        exporter.export_filtered(self.student, self.outfile)
         result = open(self.outfile, 'rb').read()
         self.assertEqual(
@@ -691,5 +690,53 @@
             'Anna M. Tester\r\n'
             )
-        # if the coursetickets catalog is used to filter students
+        # We can set the course tickets level and level_session without code
+        # (used in the datacenter)
+        notify(grok.ObjectModifiedEvent(self.student['studycourse']['100']['CRS1']))
+        exporter.export_filtered(self.student, self.outfile, ct_level='100',
+            ct_session='2012')
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(
+            result,
+            'automatic,carry_over,code,course_category,credits,dcode,fcode,level,level_session,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
+            'display_fullname\r\n'
+            '1,1,CRS1,,100,DEP1,FAC1,100,2012,0,0,100,,2,Course 1,A111111,CERT1,'
+            'Anna M. Tester\r\n'
+            )
+        # 'all' does select all
+        notify(grok.ObjectModifiedEvent(self.student['studycourse']['100']['CRS1']))
+        exporter.export_filtered(self.student, self.outfile, ct_level='all',
+            ct_session='2012')
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(
+            result,
+            'automatic,carry_over,code,course_category,credits,dcode,fcode,level,level_session,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
+            'display_fullname\r\n'
+            '1,1,CRS1,,100,DEP1,FAC1,100,2012,0,0,100,,2,Course 1,A111111,CERT1,'
+            'Anna M. Tester\r\n'
+            )
+        # Level 200 tickets do not exist.
+        notify(grok.ObjectModifiedEvent(self.student['studycourse']['100']['CRS1']))
+        exporter.export_filtered(self.student, self.outfile, ct_level='200')
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(
+            result,
+            'automatic,carry_over,code,course_category,credits,dcode,fcode,level,level_session,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
+            'display_fullname\r\n'
+                        )
+        # Session 2013 tickets do not exist.
+        notify(grok.ObjectModifiedEvent(self.student['studycourse']['100']['CRS1']))
+        exporter.export_filtered(self.student, self.outfile,
+            ct_level='all', ct_session='2013')
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(
+            result,
+            'automatic,carry_over,code,course_category,credits,dcode,fcode,level,level_session,'
+            'mandatory,outstanding,passmark,score,semester,title,student_id,certcode,'
+            'display_fullname\r\n'
+            )
+        # If the coursetickets catalog is used to filter students
         # and (course) code is not None
         # only course tickets which belong to this course are exported
