Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 12896)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 12897)
@@ -4,4 +4,6 @@
 1.3.2.dev0 (unreleased)
 =======================
+
+* Filter students by current level in reports.
 
 * Add `StudentsUtils.increaseMatricInteger` method which allows to use various
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/browser_templates/studentstatisticsreportgeneratorpage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/browser_templates/studentstatisticsreportgeneratorpage.pt	(revision 12896)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/browser_templates/studentstatisticsreportgeneratorpage.pt	(revision 12897)
@@ -40,4 +40,15 @@
   </select>
   <br />
+  <label for="level">Current Level</label>
+  <select name="level" i18n:translate="" class="form-control half">
+    <span tal:repeat="items view/levels" tal:omit-tag="">
+      <option
+          tal:define="name python: items[1]; title python: items[0]"
+          tal:attributes="value name">
+        <span tal:replace="title">TITLE</span>
+      </option>
+    </span>
+  </select>
+  <br />
   <input i18n:translate="" type="submit" class="btn btn-primary"
          name="CREATE" value="Create" />
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_report.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_report.py	(revision 12896)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_report.py	(revision 12897)
@@ -17,4 +17,5 @@
 ##
 import grok
+from zope.i18n import translate
 from zope.catalog.interfaces import ICatalog
 from zope.component import queryUtility, getUtility
@@ -26,4 +27,5 @@
 from waeup.kofa.reports import IReport
 from waeup.kofa.browser.pdf import get_signature_tables
+from waeup.kofa.students.vocabularies import StudyLevelSource
 from waeup.kofa.students.reports.student_statistics import (
     StudentStatisticsReportPDFView)
@@ -174,5 +176,5 @@
         pdf_data += [
             Table(table_data, style=TABLE_STYLE, colWidths=col_widths)]
-        doc_title = 'Level Report'
+        doc_title = translate(_('Level Report'))
 
         pdf_data.append(Spacer(1, 40))
@@ -183,5 +185,5 @@
         pdf = creator.create_pdf(
             pdf_data, None, doc_title, self.author,
-            'Level Report %s/%s/%s/%s' % (
+            doc_title + ' %s/%s/%s/%s' % (
             self.faccode, self.depcode, self.levelcode, self.sessioncode)
             )
@@ -262,7 +264,17 @@
         return
 
+    #def _set_level_values(self):
+    #    vocab_terms = course_levels.by_value.values()
+    #    self.levels = sorted([(x.title, x.token) for x in vocab_terms])
+    #    return
+
     def _set_level_values(self):
-        vocab_terms = course_levels.by_value.values()
-        self.levels = sorted([(x.title, x.token) for x in vocab_terms])
+        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
+        studylevelsource = StudyLevelSource().factory
+        self.levels = []
+        for code in studylevelsource.getValues(None):
+            title = translate(studylevelsource.getTitle(None, code),
+                'waeup.kofa', target_language=portal_language)
+            self.levels.append((title, code))
         return
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_payment_statistics.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_payment_statistics.py	(revision 12896)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_payment_statistics.py	(revision 12897)
@@ -17,4 +17,5 @@
 ##
 import grok
+from zope.i18n import translate
 from zope.catalog.interfaces import ICatalog
 from zope.component import queryUtility, getUtility
@@ -23,4 +24,5 @@
     IKofaUtils,
     academic_sessions_vocab, registration_states_vocab)
+from waeup.kofa.students.vocabularies import StudyLevelSource
 from waeup.kofa.interfaces import MessageFactory as _
 from waeup.kofa.reports import IReport
@@ -31,9 +33,11 @@
     session = Attribute('Session to report')
     mode = Attribute('Study modes group to report')
+    level = Attribute('Level to report')
     creation_dt_string = Attribute('Human readable report creation datetime')
 
 
-def get_student_payment_stats(session, mode, breakdown):
-    """Get students in a certain session and study mode.
+def get_student_payment_stats(session, mode, level, breakdown):
+    """Get payment amounts of students in a certain session, study mode
+    and current level.
 
     Returns a table ordered by code (faculty or department, one per row) and
@@ -83,4 +87,6 @@
         if mode != 'All' and stud.current_mode not in mode_groups[mode]:
             continue
+        if level != 0 and stud.current_level != level:
+            continue
         if getattr(stud, breakdown) not in codes:
             # studs can have a faccode ``None``
@@ -176,14 +182,21 @@
     mode = None
 
-    def __init__(self, session, mode, breakdown, author='System'):
+    def __init__(self, session, mode, level, breakdown, author='System'):
         super(StudentPaymentStatisticsReport, self).__init__(
             args=[session, mode, breakdown], kwargs={'author':author})
+        self.studylevelsource = StudyLevelSource().factory
+        self.portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
         self.session = academic_sessions_vocab.getTerm(session).title
         self.mode = mode
+        if level == 0:
+            self.level = ''
+        else:
+            self.level = translate(self.studylevelsource.getTitle(None, int(level)),
+                'waeup.kofa', target_language=self.portal_language)
         self.breakdown = breakdown
         self.author = author
         self.creation_dt_string = self.creation_dt.astimezone(
             getUtility(IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
-        self.data = get_student_payment_stats(session, mode, breakdown)
+        self.data = get_student_payment_stats(session, mode, level, breakdown)
 
     def create_pdf(self):
@@ -196,9 +209,18 @@
         pdf_data += [
             Table(table_data, style=TABLE_STYLE, colWidths=col_widths)]
-        doc_title = '%s Student Payments in Session %s' % (self.mode, self.session)
-        pdf = creator.create_pdf(
-            pdf_data, None, doc_title, self.author,
-            'Student Payments in Session %s' % self.session
-            )
+        if not self.level:
+            doc_title = translate(
+                _('${a} Student Payments - ${b}',
+                mapping = {'a':self.mode, 'b':self.session}))
+            pdf = creator.create_pdf(
+                pdf_data, None, doc_title, self.author, doc_title + '-'
+                )
+        else:
+            doc_title = translate(
+                _('${a} Student Payments - ${b} - ${c}',
+                mapping = {'a':self.mode, 'b': self.level, 'c':self.session}))
+            pdf = creator.create_pdf(
+                pdf_data, None, doc_title, self.author, doc_title + '-'
+                )
         return pdf
 
@@ -210,8 +232,10 @@
     grok.name('student_payment_stats')
 
-    def generate(self, site, session=None,
-                 mode=None, breakdown=None, author=None):
+    def generate(
+        self, site, session=None, mode=None,
+        level=None, breakdown=None, author=None):
         result = StudentPaymentStatisticsReport(
-            session=session, mode=mode, breakdown=breakdown, author=author)
+            session=session, mode=mode, level=level,
+            breakdown=breakdown, author=author)
         return result
 
@@ -245,6 +269,6 @@
 
     def _filename(self):
-        return 'StudentPaymentStatisticsReport_%s_%s_%s.pdf' % (
-            self.context.session, self.context.mode,
+        return 'StudentPaymentStatisticsReport_%s_%s_%s_%s.pdf' % (
+            self.context.session, self.context.mode, self.context.level,
             self.context.creation_dt_string)
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_statistics.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_statistics.py	(revision 12896)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/student_statistics.py	(revision 12897)
@@ -17,4 +17,5 @@
 ##
 import grok
+from zope.i18n import translate
 from zope.catalog.interfaces import ICatalog
 from zope.component import queryUtility, getUtility
@@ -23,4 +24,5 @@
     IKofaUtils,
     academic_sessions_vocab, registration_states_vocab)
+from waeup.kofa.students.vocabularies import StudyLevelSource
 from waeup.kofa.interfaces import MessageFactory as _
 from waeup.kofa.reports import IReport
@@ -29,9 +31,10 @@
 
     session = Attribute('Session to report')
+    level = Attribute('Level to report')
     mode = Attribute('Study modes group to report')
     creation_dt_string = Attribute('Human readable report creation datetime')
 
-def get_student_stats(session, mode, breakdown):
-    """Get students in a certain session and study mode.
+def get_student_stats(session, mode, level, breakdown):
+    """Get students in a certain session, study mode and current level.
 
     Returns a table ordered by code (faculty or department, one per row) and
@@ -81,4 +84,6 @@
         if mode != 'All' and stud.current_mode not in mode_groups[mode]:
             continue
+        if level != 0 and stud.current_level != level:
+            continue
         if getattr(stud, breakdown) not in codes:
             # studs can have a faccode ``None``
@@ -139,14 +144,21 @@
     mode = None
 
-    def __init__(self, session, mode, breakdown, author='System'):
+    def __init__(self, session, mode, level, breakdown, author='System'):
         super(StudentStatisticsReport, self).__init__(
             args=[session, mode, breakdown], kwargs={'author':author})
+        self.studylevelsource = StudyLevelSource().factory
+        self.portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
         self.session = academic_sessions_vocab.getTerm(session).title
         self.mode = mode
+        if level == 0:
+            self.level = ''
+        else:
+            self.level = translate(self.studylevelsource.getTitle(None, int(level)),
+                'waeup.kofa', target_language=self.portal_language)
         self.breakdown = breakdown
         self.author = author
         self.creation_dt_string = self.creation_dt.astimezone(
             getUtility(IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
-        self.data = get_student_stats(session, mode, breakdown)
+        self.data = get_student_stats(session, mode, level, breakdown)
 
     def create_pdf(self):
@@ -159,9 +171,18 @@
         pdf_data += [
             Table(table_data, style=TABLE_STYLE, colWidths=col_widths)]
-        doc_title = '%s Students in Session %s' % (self.mode, self.session)
-        pdf = creator.create_pdf(
-            pdf_data, None, doc_title, self.author,
-            'Students in Session %s' % self.session
-            )
+        if not self.level:
+            doc_title = translate(
+                _('${a} Students - ${b}',
+                mapping = {'a':self.mode, 'b':self.session}))
+            pdf = creator.create_pdf(
+                pdf_data, None, doc_title, self.author, doc_title + '-'
+                )
+        else:
+            doc_title = translate(
+                _('${a} Students - ${b} - ${c}',
+                mapping = {'a':self.mode, 'b': self.level, 'c':self.session}))
+            pdf = creator.create_pdf(
+                pdf_data, None, doc_title, self.author, doc_title + '-'
+                )
         return pdf
 
@@ -172,7 +193,10 @@
     grok.name('student_stats')
 
-    def generate(self, site, session=None, mode=None, breakdown=None, author=None):
+    def generate(
+        self, site, session=None, mode=None,
+        level=None, breakdown=None, author=None):
         result = StudentStatisticsReport(
-            session=session, mode=mode, breakdown=breakdown, author=author)
+            session=session, mode=mode, level=level,
+            breakdown=breakdown, author=author)
         return result
 
@@ -202,11 +226,13 @@
         return None
 
-    def update(self, CREATE=None, session=None, mode=None, breakdown=None):
+    def update(
+        self, CREATE=None, session=None, mode=None, level=None, breakdown=None):
         self.parent_url = self.url(self.context.__parent__)
         self._set_session_values()
         self._set_mode_values()
+        self._set_level_values()
         self._set_breakdown_values()
         if CREATE and session:
-            # create a new report job for students by session
+            # create a new report job for students by session and level
             container = self.context.__parent__
             user_id = self.request.principal.id
@@ -214,4 +240,5 @@
                 session=int(session),
                 mode=mode,
+                level=int(level),
                 breakdown=breakdown)
             self.flash(_('New report is being created in background'))
@@ -220,6 +247,7 @@
             ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
             grok.getSite().logger.info(
-                '%s - report %s created: %s (session=%s, mode=%s, breakdown=%s)' % (
-                ob_class, job_id, self.context.title, session, mode, breakdown))
+                '%s - report %s created: %s (session=%s, mode=%s, level=%s, breakdown=%s)' % (
+                ob_class, job_id, self.context.title,
+                session, mode, level, breakdown))
             self.redirect(self.parent_url)
             return
@@ -234,4 +262,14 @@
         mode_groups = getUtility(IKofaUtils).MODE_GROUPS
         self.modes = sorted([(key, key) for key in mode_groups.keys()])
+        return
+
+    def _set_level_values(self):
+        portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
+        studylevelsource = StudyLevelSource().factory
+        self.levels = [(u'All levels', 0)]
+        for code in studylevelsource.getValues(None):
+            title = translate(studylevelsource.getTitle(None, code),
+                'waeup.kofa', target_language=portal_language)
+            self.levels.append((title, code))
         return
 
@@ -248,6 +286,6 @@
 
     def _filename(self):
-        return 'StudentStatisticsReport_%s_%s_%s.pdf' % (
-            self.context.session, self.context.mode,
+        return 'StudentStatisticsReport_%s_%s_%s_%s.pdf' % (
+            self.context.session, self.context.mode, self.context.level,
             self.context.creation_dt_string)
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_payment_statistics.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_payment_statistics.py	(revision 12896)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_payment_statistics.py	(revision 12897)
@@ -22,5 +22,5 @@
         # ensure we fullfill interface contracts
         obj = StudentPaymentStatisticsReport(
-            2010, 'Undergraduate Full-Time', 'faccode')
+            2010, 'Undergraduate Full-Time', 0, 'faccode')
         verifyClass(IStudentPaymentStatisticsReport, StudentPaymentStatisticsReport)
         verifyObject(IStudentPaymentStatisticsReport, obj)
@@ -29,6 +29,8 @@
     def test_get_student_payment_stats_session_simple(self):
         # we can get a table with one student
-        result1 = get_student_payment_stats(2010, 'Undergraduate Full-Time', 'faccode')
-        result2 = get_student_payment_stats(2009, 'Undergraduate Full-Time', 'faccode')
+        result1 = get_student_payment_stats(
+            2010, 'Undergraduate Full-Time', 0, 'faccode')
+        result2 = get_student_payment_stats(
+            2009, 'Undergraduate Full-Time', 0, 'faccode')
         self.assertEqual(
             result1,
@@ -53,5 +55,5 @@
     def test_create_pdf(self):
         report = StudentPaymentStatisticsReport(
-            2010, 'Undergraduate Full-Time', 'faccode')
+            2010, 'Undergraduate Full-Time', 0,  'faccode')
         result = report.create_pdf()
         self.assertTrue(result.startswith('%PDF-'))
@@ -104,5 +106,5 @@
                          'application/pdf')
         self.assertTrue(
-            'filename="StudentPaymentStatisticsReport_2004_2005_All_' in
+            'filename="StudentPaymentStatisticsReport_2004_2005_All__' in
             self.browser.headers['content-disposition'])
         self.assertEqual(len(self.app['reports'].running_report_jobs), 1)
@@ -118,10 +120,10 @@
         self.assertTrue(
             'INFO - zope.mgr - students.reports.student_payment_statistics.StudentPaymentStatisticsReportGeneratorPage - '
-            'report %s created: Student Payment Statistics (session=2004, mode=All, breakdown=depcode)'
+            'report %s created: Student Payment Statistics (session=2004, mode=All, level=0, breakdown=depcode)'
             % job_id in logcontent
             )
         self.assertTrue(
             'INFO - zope.mgr - students.reports.student_payment_statistics.StudentPaymentStatisticsReportPDFView - '
-            'report %s downloaded: StudentPaymentStatisticsReport_2004_2005_All_'
+            'report %s downloaded: StudentPaymentStatisticsReport_2004_2005_All__'
             % job_id in logcontent
             )
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_statistics.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_statistics.py	(revision 12896)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_student_statistics.py	(revision 12897)
@@ -25,5 +25,5 @@
         # ensure we fullfill interface contracts
         obj = StudentStatisticsReport(
-            2010, 'Undergraduate Full-Time', 'faccode')
+            2010, 'Undergraduate Full-Time', 0, 'faccode')
         verifyClass(IStudentStatisticsReport, StudentStatisticsReport)
         verifyObject(IStudentStatisticsReport, obj)
@@ -32,6 +32,8 @@
     def test_get_student_stats_session_simple(self):
         # we can get a table with one student
-        result1 = get_student_stats(2010, 'Undergraduate Full-Time', 'faccode')
-        result2 = get_student_stats(2009, 'Undergraduate Full-Time', 'faccode')
+        result1 = get_student_stats(
+            2010, 'Undergraduate Full-Time', 0, 'faccode')
+        result2 = get_student_stats(
+            2009, 'Undergraduate Full-Time', 0, 'faccode')
         self.assertEqual(
             result1,
@@ -51,6 +53,8 @@
         # we can get a table with several students
         self.create_cert(u'fac2', u'dep2', u'CERT2')
-        result1 = get_student_stats(2010, 'Undergraduate Full-Time', 'faccode')
-        result2 = get_student_stats(2009, 'Undergraduate Full-Time', 'faccode')
+        result1 = get_student_stats(
+            2010, 'Undergraduate Full-Time', 0, 'faccode')
+        result2 = get_student_stats(
+            2009, 'Undergraduate Full-Time', 0, 'faccode')
         self.assertEqual(
             result1,
@@ -72,6 +76,27 @@
         # we can get a table with several students
         self.create_cert(u'fac2', u'dep2', u'CERT2')
-        result1 = get_student_stats(2010, 'Undergraduate Full-Time', 'depcode')
-        result2 = get_student_stats(2009, 'Undergraduate Full-Time', 'depcode')
+        result1 = get_student_stats(2010, 'Undergraduate Full-Time', 0, 'depcode')
+        result2 = get_student_stats(2009, 'Undergraduate Full-Time', 0, 'depcode')
+        self.assertEqual(
+            result1,
+            ((u'fac1/dep1', u'fac2/dep2', u'Total'),
+             self.states,
+             ((1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),
+              (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+              (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1),)))
+        self.assertEqual(
+            result2,
+            ((u'fac1/dep1', u'fac2/dep2', u'Total'),
+             self.states,
+             ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+              (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+              (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),)))
+        return
+
+    def test_get_student_stats_session_multiple_dep_breakdown_level_100(self):
+        # we can get a table with several students
+        self.create_cert(u'fac2', u'dep2', u'CERT2')
+        result1 = get_student_stats(2010, 'Undergraduate Full-Time', 0, 'depcode')
+        result2 = get_student_stats(2009, 'Undergraduate Full-Time', 0, 'depcode')
         self.assertEqual(
             result1,
@@ -93,5 +118,5 @@
         self.create_cert(u'FAC2', u'dept2', u'CERT2')
         report = StudentStatisticsReport(
-            2010, 'Undergraduate Full-Time', 'faccode')
+            2010, 'Undergraduate Full-Time', 0, 'faccode')
         result = report.create_pdf()
         self.assertTrue(result.startswith('%PDF-'))
@@ -99,4 +124,15 @@
         open(path, 'wb').write(result)
         print "Sample PDF student_statistics.pdf written to %s" % path
+        return
+
+    def test_create_100level_pdf(self):
+        self.create_cert(u'FAC2', u'dept2', u'CERT2')
+        report = StudentStatisticsReport(
+            2010, 'Undergraduate Full-Time', 100, 'faccode')
+        result = report.create_pdf()
+        self.assertTrue(result.startswith('%PDF-'))
+        path = os.path.join(samples_dir(), 'student_statistics_100.pdf')
+        open(path, 'wb').write(result)
+        print "Sample PDF student_statistics_100.pdf written to %s" % path
         return
 
@@ -158,10 +194,10 @@
         self.assertTrue(
             'INFO - zope.mgr - students.reports.student_statistics.StudentStatisticsReportGeneratorPage - '
-            'report %s created: Student Statistics (session=2004, mode=All, breakdown=depcode)'
+            'report %s created: Student Statistics (session=2004, mode=All, level=0, breakdown=depcode)'
             % job_id in logcontent
             )
         self.assertTrue(
             'INFO - zope.mgr - students.reports.student_statistics.StudentStatisticsReportPDFView - '
-            'report %s downloaded: StudentStatisticsReport_2004_2005_All_'
+            'report %s downloaded: StudentStatisticsReport_2004_2005_All__'
             % job_id in logcontent
             )
