Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_results_presentation.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/level_results_presentation.py	(revision 10576)
+++ 	(revision )
@@ -1,199 +1,0 @@
-## $Id$
-##
-## Copyright (C) 2013 Uli Fouquet & Henrik Bettermann
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 of the License, or
-## (at your option) any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-##
-import grok
-from zope.catalog.interfaces import ICatalog
-from zope.component import queryUtility, getUtility
-from zope.interface import implementer, Interface, Attribute
-from waeup.kofa.interfaces import (
-    IKofaUtils,
-    academic_sessions_vocab, registration_states_vocab)
-from waeup.kofa.students.interfaces import IStudentsUtils
-from waeup.kofa.interfaces import MessageFactory as _
-from waeup.kofa.browser.pdf import get_signature_tables
-from waeup.kofa.reports import IReport
-from waeup.kofa.students.reports.level_report import (
-    ILevelReport, LevelReportGeneratorPage)
-from waeup.kofa.students.reports.student_statistics import (
-    StudentStatisticsReportPDFView)
-
-
-class ISessionResultsPresentation(ILevelReport):
-
-    """ Same interface as for level reports.
-    """
-
-def get_students(faccode, depcode, session, level=None):
-    """Get students in a certain department who registered courses
-    in a certain session at a certain level.
-
-    Returns a list of lists of student data tuples.
-    """
-    site = grok.getSite()
-    cat = queryUtility(ICatalog, name="students_catalog")
-    result = cat.searchResults(
-        depcode = (depcode, depcode), faccode = (faccode, faccode)
-        )
-    students_utils = getUtility(IStudentsUtils)
-    table = []
-    for i in range(len(students_utils.gpa_boundaries)+1):
-        # The last list is reserved for students with more than one
-        # level in the same session.
-        table.append([])
-    for stud in result:
-        line = (stud.student_id,
-                stud.matric_number,
-                stud.display_fullname,
-                )
-        if level is not None:
-            if not stud['studycourse'].has_key(str(level)):
-                continue
-            level_obj = stud['studycourse'][str(level)]
-            if level_obj.level_session != session:
-                continue
-        else:
-            # XXX: Not yet supported by by generator page
-            itemcount = 0
-            for item in stud['studycourse'].values():
-                if item.level_session == session:
-                    level_obj = item
-                    itemcount += 1
-            if itemcount == 0:
-                # No level registered in this session
-                continue
-            if itemcount > 1:
-                # More than one session registered in this session
-                table[len(students_utils.gpa_boundaries)].append(line)
-                continue
-        table[students_utils.getClassFromCGPA(level_obj.gpa)[0]].append(line)
-    return table
-
-from reportlab.lib import colors
-from reportlab.lib.styles import getSampleStyleSheet
-from reportlab.lib.units import cm
-from reportlab.platypus import Paragraph, Table, Spacer
-from reportlab.platypus.flowables import Flowable
-from waeup.kofa.reports import IReport, IReportGenerator
-from waeup.kofa.reports import Report
-from waeup.kofa.browser.interfaces import IPDFCreator
-from waeup.kofa.university.vocabularies import course_levels
-
-STYLE = getSampleStyleSheet()
-
-TABLE_STYLE = [
-    ('FONT', (0,0), (-1,-1), 'Helvetica', 8),
-    ('FONT', (0,0), (-1,0), 'Helvetica-Bold', 8), # 1st row
-    ('ALIGN', (3,1), (-1,-1), 'RIGHT'),
-    #('ALIGN', (1,0), (1,-1), 'LEFT'),
-    #('ALIGN', (2,0), (2,-1), 'LEFT'),
-    #('INNERGRID', (0,1), (-1,-1), 0.25, colors.black),
-    #('BOX', (0,1), (-1,-1), 1, colors.black),
-    ]
-
-@implementer(ISessionResultsPresentation)
-class SessionResultsPresentation(Report):
-    data = None
-    session = None
-    level = None
-    faccode = None
-    depcode = None
-
-    def __init__(self, faccode, depcode, session, level, author='System'):
-        super(SessionResultsPresentation, self).__init__(
-            args=[faccode, depcode, session, level], kwargs={'author':author})
-        site = grok.getSite()
-        self.session = academic_sessions_vocab.getTerm(session).title
-        self.level = course_levels.getTerm(level).title
-        self.levelcode = level
-        self.sessioncode = session
-        self.faccode = faccode
-        self.depcode = depcode
-        self.factitle = site['faculties'][faccode].longtitle()
-        self.deptitle = site['faculties'][faccode][depcode].longtitle()
-        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_students(faccode, depcode, session, level)
-
-    def create_pdf(self):
-        creator = getUtility(IPDFCreator)
-        #col_widths = [2*cm, 4*cm, 5*cm, 0.8*cm, 0.8*cm, 0.8*cm, 6*cm, ]
-        pdf_data = [Paragraph('<b>%s</b>' % self.creation_dt_string,
-                              STYLE["Normal"]),
-                    Spacer(1, 12),]
-        pdf_data += [Paragraph('%s<br />%s<br />Level: %s<br />Session: %s' % (
-                    self.factitle, self.deptitle, self.level, self.session),
-                    STYLE["Normal"]),
-                    Spacer(1, 12),]
-
-        for table in self.data:
-            table_data = [('Student Id', 'Matric No.', 'Name')] + table
-            pdf_data += [
-                Table(table_data, style=TABLE_STYLE)]    #, colWidths=col_widths)]
-        doc_title = 'Presentation of Session Results'
-
-        pdf_data.append(Spacer(1, 40))
-        signatures = ['Ag. Head of Department', 'External Examiner', 'Dean of Faculty']
-        signaturetables = get_signature_tables(signatures)
-        pdf_data.append(signaturetables[0])
-
-        pdf = creator.create_pdf(
-            pdf_data, None, doc_title, self.author,
-            'Session Results Presentation %s/%s/%s/%s' % (
-            self.faccode, self.depcode, self.levelcode, self.sessioncode)
-            )
-        return pdf
-
-@implementer(IReportGenerator)
-class SessionResultsPresentationGenerator(grok.GlobalUtility):
-
-    title = _('Session Results Presentation')
-    grok.name('session_results_presentation')
-
-    def generate(self, site, faccode=None, depcode=None,
-                 session=None, level=None, author=None):
-        result = SessionResultsPresentation(faccode=faccode, depcode=depcode,
-                             session=session, level=level, author=author)
-        return result
-
-###############################################################
-## Browser related stuff
-##
-## XXX: move to local browser module
-###############################################################
-from waeup.kofa.browser.layout import KofaPage
-from waeup.kofa.interfaces import academic_sessions_vocab
-from waeup.kofa.reports import get_generators
-grok.templatedir('browser_templates')
-class SessionResultsPresentationGeneratorPage(LevelReportGeneratorPage):
-
-    grok.context(SessionResultsPresentationGenerator)
-    grok.template('levelreportgeneratorpage')
-    label = _('Create session results presentation')
-
-class SessionResultsPresentationPDFView(StudentStatisticsReportPDFView):
-
-    grok.context(ISessionResultsPresentation)
-    grok.name('pdf')
-    grok.require('waeup.Public')
-    prefix = 'SessionResultsPresentation'
-
-    def _filename(self):
-        return 'SessionResultsPresentation_%s_%s_%s_%s_%s.pdf' % (
-            self.context.faccode, self.context.depcode,
-            self.context.sessioncode, self.context.levelcode,
-            self.context.creation_dt_string)
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/session_results_presentation.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/session_results_presentation.py	(revision 10577)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/session_results_presentation.py	(revision 10577)
@@ -0,0 +1,199 @@
+## $Id$
+##
+## Copyright (C) 2013 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+import grok
+from zope.catalog.interfaces import ICatalog
+from zope.component import queryUtility, getUtility
+from zope.interface import implementer, Interface, Attribute
+from waeup.kofa.interfaces import (
+    IKofaUtils,
+    academic_sessions_vocab, registration_states_vocab)
+from waeup.kofa.students.interfaces import IStudentsUtils
+from waeup.kofa.interfaces import MessageFactory as _
+from waeup.kofa.browser.pdf import get_signature_tables
+from waeup.kofa.reports import IReport
+from waeup.kofa.students.reports.level_report import (
+    ILevelReport, LevelReportGeneratorPage)
+from waeup.kofa.students.reports.student_statistics import (
+    StudentStatisticsReportPDFView)
+
+
+class ISessionResultsPresentation(ILevelReport):
+
+    """ Same interface as for level reports.
+    """
+
+def get_students(faccode, depcode, session, level=None):
+    """Get students in a certain department who registered courses
+    in a certain session at a certain level.
+
+    Returns a list of lists of student data tuples.
+    """
+    site = grok.getSite()
+    cat = queryUtility(ICatalog, name="students_catalog")
+    result = cat.searchResults(
+        depcode = (depcode, depcode), faccode = (faccode, faccode)
+        )
+    students_utils = getUtility(IStudentsUtils)
+    table = []
+    for i in range(len(students_utils.gpa_boundaries)+1):
+        # The last list is reserved for students with more than one
+        # level in the same session.
+        table.append([])
+    for stud in result:
+        line = (stud.student_id,
+                stud.matric_number,
+                stud.display_fullname,
+                )
+        if level is not None:
+            if not stud['studycourse'].has_key(str(level)):
+                continue
+            level_obj = stud['studycourse'][str(level)]
+            if level_obj.level_session != session:
+                continue
+        else:
+            # XXX: Not yet supported by by generator page
+            itemcount = 0
+            for item in stud['studycourse'].values():
+                if item.level_session == session:
+                    level_obj = item
+                    itemcount += 1
+            if itemcount == 0:
+                # No level registered in this session
+                continue
+            if itemcount > 1:
+                # More than one session registered in this session
+                table[len(students_utils.gpa_boundaries)].append(line)
+                continue
+        table[students_utils.getClassFromCGPA(level_obj.gpa)[0]].append(line)
+    return table
+
+from reportlab.lib import colors
+from reportlab.lib.styles import getSampleStyleSheet
+from reportlab.lib.units import cm
+from reportlab.platypus import Paragraph, Table, Spacer
+from reportlab.platypus.flowables import Flowable
+from waeup.kofa.reports import IReport, IReportGenerator
+from waeup.kofa.reports import Report
+from waeup.kofa.browser.interfaces import IPDFCreator
+from waeup.kofa.university.vocabularies import course_levels
+
+STYLE = getSampleStyleSheet()
+
+TABLE_STYLE = [
+    ('FONT', (0,0), (-1,-1), 'Helvetica', 8),
+    ('FONT', (0,0), (-1,0), 'Helvetica-Bold', 8), # 1st row
+    ('ALIGN', (3,1), (-1,-1), 'RIGHT'),
+    #('ALIGN', (1,0), (1,-1), 'LEFT'),
+    #('ALIGN', (2,0), (2,-1), 'LEFT'),
+    #('INNERGRID', (0,1), (-1,-1), 0.25, colors.black),
+    #('BOX', (0,1), (-1,-1), 1, colors.black),
+    ]
+
+@implementer(ISessionResultsPresentation)
+class SessionResultsPresentation(Report):
+    data = None
+    session = None
+    level = None
+    faccode = None
+    depcode = None
+
+    def __init__(self, faccode, depcode, session, level, author='System'):
+        super(SessionResultsPresentation, self).__init__(
+            args=[faccode, depcode, session, level], kwargs={'author':author})
+        site = grok.getSite()
+        self.session = academic_sessions_vocab.getTerm(session).title
+        self.level = course_levels.getTerm(level).title
+        self.levelcode = level
+        self.sessioncode = session
+        self.faccode = faccode
+        self.depcode = depcode
+        self.factitle = site['faculties'][faccode].longtitle()
+        self.deptitle = site['faculties'][faccode][depcode].longtitle()
+        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_students(faccode, depcode, session, level)
+
+    def create_pdf(self):
+        creator = getUtility(IPDFCreator)
+        #col_widths = [2*cm, 4*cm, 5*cm, 0.8*cm, 0.8*cm, 0.8*cm, 6*cm, ]
+        pdf_data = [Paragraph('<b>%s</b>' % self.creation_dt_string,
+                              STYLE["Normal"]),
+                    Spacer(1, 12),]
+        pdf_data += [Paragraph('%s<br />%s<br />Level: %s<br />Session: %s' % (
+                    self.factitle, self.deptitle, self.level, self.session),
+                    STYLE["Normal"]),
+                    Spacer(1, 12),]
+
+        for table in self.data:
+            table_data = [('Student Id', 'Matric No.', 'Name')] + table
+            pdf_data += [
+                Table(table_data, style=TABLE_STYLE)]    #, colWidths=col_widths)]
+        doc_title = 'Presentation of Session Results'
+
+        pdf_data.append(Spacer(1, 40))
+        signatures = ['Ag. Head of Department', 'External Examiner', 'Dean of Faculty']
+        signaturetables = get_signature_tables(signatures)
+        pdf_data.append(signaturetables[0])
+
+        pdf = creator.create_pdf(
+            pdf_data, None, doc_title, self.author,
+            'Session Results Presentation %s/%s/%s/%s' % (
+            self.faccode, self.depcode, self.levelcode, self.sessioncode)
+            )
+        return pdf
+
+@implementer(IReportGenerator)
+class SessionResultsPresentationGenerator(grok.GlobalUtility):
+
+    title = _('Session Results Presentation')
+    grok.name('session_results_presentation')
+
+    def generate(self, site, faccode=None, depcode=None,
+                 session=None, level=None, author=None):
+        result = SessionResultsPresentation(faccode=faccode, depcode=depcode,
+                             session=session, level=level, author=author)
+        return result
+
+###############################################################
+## Browser related stuff
+##
+## XXX: move to local browser module
+###############################################################
+from waeup.kofa.browser.layout import KofaPage
+from waeup.kofa.interfaces import academic_sessions_vocab
+from waeup.kofa.reports import get_generators
+grok.templatedir('browser_templates')
+class SessionResultsPresentationGeneratorPage(LevelReportGeneratorPage):
+
+    grok.context(SessionResultsPresentationGenerator)
+    grok.template('levelreportgeneratorpage')
+    label = _('Create session results presentation')
+
+class SessionResultsPresentationPDFView(StudentStatisticsReportPDFView):
+
+    grok.context(ISessionResultsPresentation)
+    grok.name('pdf')
+    grok.require('waeup.Public')
+    prefix = 'SessionResultsPresentation'
+
+    def _filename(self):
+        return 'SessionResultsPresentation_%s_%s_%s_%s_%s.pdf' % (
+            self.context.faccode, self.context.depcode,
+            self.context.sessioncode, self.context.levelcode,
+            self.context.creation_dt_string)
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_level_results_presentation.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_level_results_presentation.py	(revision 10576)
+++ 	(revision )
@@ -1,135 +1,0 @@
-import os
-from zc.async.testing import wait_for_result
-from zope.event import notify
-from zope.interface.verify import verifyClass, verifyObject
-from zope.component import getUtility
-from waeup.kofa.interfaces import IJobManager
-from waeup.kofa.students.reports.level_results_presentation import (
-    get_students, SessionResultsPresentation, ISessionResultsPresentation)
-from waeup.kofa.students.tests.test_catalog import CatalogTestSetup
-from waeup.kofa.students.tests.test_browser import StudentsFullSetup
-from waeup.kofa.testing import FunctionalLayer
-from waeup.kofa.tests.test_async import FunctionalAsyncTestCase
-from waeup.kofa.browser.tests.test_pdf import samples_dir
-from waeup.kofa.students.studylevel import StudentStudyLevel
-
-class SessionResultsPresentationTests(CatalogTestSetup):
-
-    layer = FunctionalLayer
-
-    def test_iface(self):
-        # ensure we fullfill interface contracts
-        obj = SessionResultsPresentation('fac1', 'dep1', 2010, 100)
-        verifyClass(ISessionResultsPresentation, SessionResultsPresentation)
-        verifyObject(ISessionResultsPresentation, obj)
-        return
-
-    def test_get_students(self):
-        result = get_students('fac1', 'dep1', 2010, 100)
-        self.assertEqual(result,
-            [[], [], [], [], [], [(u'K1000000', u'1234', u'Bob Tester')], []])
-        return
-
-    def test_get_students_at_all_levels(self):
-        result = get_students('fac1', 'dep1', 2010)
-        self.assertEqual(result,
-            [[], [], [], [], [], [(u'K1000000', u'1234', u'Bob Tester')], []])
-        return
-
-    def test_get_students_with_two_levels_in_session(self):
-        # Register second level in the same session (which shouldn't be!)
-        studylevel = StudentStudyLevel()
-        studylevel.level = 200
-        studylevel.level_session = 2010
-        self.student['studycourse']['200'] = studylevel
-        result = get_students('fac1', 'dep1', 2010)
-        self.assertEqual(result,
-            [[], [], [], [], [], [], [(u'K1000000', u'1234', u'Bob Tester')]])
-        return
-
-    def test_get_students_without_level_in_session(self):
-        self.student['studycourse']['100'].level_session = 2011
-        result = get_students('fac1', 'dep1', 2010)
-        self.assertEqual(result,
-            [[], [], [], [], [], [], []])
-        return
-
-    def test_create_pdf(self):
-        report = SessionResultsPresentation('fac1', 'dep1', 2010, 100)
-        result = report.create_pdf()
-        self.assertTrue(result.startswith('%PDF-'))
-        path = os.path.join(samples_dir(), 'session_results_presentation.pdf')
-        open(path, 'wb').write(result)
-        print "Sample session_results_presentation.pdf written to %s" % path
-        return
-
-class SessionResultsPresentationUITests(StudentsFullSetup, FunctionalAsyncTestCase):
-
-    layer = FunctionalLayer
-
-    def wait_for_report_job_completed(self):
-        # helper function waiting until the current export job is completed
-        manager = getUtility(IJobManager)
-        job_id = self.app['reports'].running_report_jobs[0][0]
-        job = manager.get(job_id)
-        wait_for_result(job)
-        return job_id
-
-    def stored_in_reports(self, job_id):
-        # tell whether job_id is stored in reports's running jobs list
-        for entry in list(self.app['reports'].running_report_jobs):
-            if entry[0] == job_id:
-                return True
-        return False
-
-    def trigger_report_creation(self):
-        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
-        self.browser.open('http://localhost/app/reports')
-        self.assertEqual(self.browser.headers['Status'], '200 Ok')
-        self.browser.getLink("Create new report").click()
-        self.browser.getControl(name="generator").value = ['session_results_presentation']
-        self.browser.getControl("Configure").click()
-        self.browser.getControl(name="level").value = ['100']
-        self.browser.getControl(name="session").value = ['2010']
-        self.browser.getControl(name="faccode_depcode").value = ['fac1_dep1']
-        self.browser.getControl("Create").click()
-        return
-
-    def test_report_download(self):
-        # We can download a generated report
-        self.trigger_report_creation()
-        # When the job is finished and we reload the page...
-        job_id = self.wait_for_report_job_completed()
-        self.browser.open('http://localhost/app/reports')
-        # ... the pdf file can be downloaded ...
-        self.browser.getControl("Download").click()
-        self.assertEqual(self.browser.headers['content-type'],
-                         'application/pdf')
-        self.assertTrue(
-            'filename="SessionResultsPresentation_fac1_dep1_2010_100_' in
-            self.browser.headers['content-disposition'])
-        self.assertEqual(len(self.app['reports'].running_report_jobs), 1)
-        job_id = self.app['reports'].running_report_jobs[0][0]
-        # ... and discarded
-        self.browser.open('http://localhost/app/reports')
-        self.browser.getControl("Discard").click()
-        self.assertEqual(len(self.app['reports'].running_report_jobs), 0)
-        # Creation, downloading and discarding is logged
-        logfile = os.path.join(
-            self.app['datacenter'].storage, 'logs', 'main.log')
-        logcontent = open(logfile).read()
-        self.assertTrue(
-            'INFO - zope.mgr - students.reports.level_results_presentation.SessionResultsPresentationGeneratorPage - '
-            'report %s created: Session Results Presentation (faculty=fac1, department=dep1, session=2010, level=100)'
-            % job_id in logcontent
-            )
-        self.assertTrue(
-            'INFO - zope.mgr - students.reports.level_results_presentation.SessionResultsPresentationPDFView - '
-            'report %s downloaded: SessionResultsPresentation_fac1_dep1_2010_100_'
-            % job_id in logcontent
-            )
-        self.assertTrue(
-            'INFO - zope.mgr - browser.reports.ReportsContainerPage - '
-            'report %s discarded' % job_id in logcontent
-            )
-        return
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_session_results_presentation.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_session_results_presentation.py	(revision 10577)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/reports/tests/test_session_results_presentation.py	(revision 10577)
@@ -0,0 +1,135 @@
+import os
+from zc.async.testing import wait_for_result
+from zope.event import notify
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import getUtility
+from waeup.kofa.interfaces import IJobManager
+from waeup.kofa.students.reports.session_results_presentation import (
+    get_students, SessionResultsPresentation, ISessionResultsPresentation)
+from waeup.kofa.students.tests.test_catalog import CatalogTestSetup
+from waeup.kofa.students.tests.test_browser import StudentsFullSetup
+from waeup.kofa.testing import FunctionalLayer
+from waeup.kofa.tests.test_async import FunctionalAsyncTestCase
+from waeup.kofa.browser.tests.test_pdf import samples_dir
+from waeup.kofa.students.studylevel import StudentStudyLevel
+
+class SessionResultsPresentationTests(CatalogTestSetup):
+
+    layer = FunctionalLayer
+
+    def test_iface(self):
+        # ensure we fullfill interface contracts
+        obj = SessionResultsPresentation('fac1', 'dep1', 2010, 100)
+        verifyClass(ISessionResultsPresentation, SessionResultsPresentation)
+        verifyObject(ISessionResultsPresentation, obj)
+        return
+
+    def test_get_students(self):
+        result = get_students('fac1', 'dep1', 2010, 100)
+        self.assertEqual(result,
+            [[], [], [], [], [], [(u'K1000000', u'1234', u'Bob Tester')], []])
+        return
+
+    def test_get_students_at_all_levels(self):
+        result = get_students('fac1', 'dep1', 2010)
+        self.assertEqual(result,
+            [[], [], [], [], [], [(u'K1000000', u'1234', u'Bob Tester')], []])
+        return
+
+    def test_get_students_with_two_levels_in_session(self):
+        # Register second level in the same session (which shouldn't be!)
+        studylevel = StudentStudyLevel()
+        studylevel.level = 200
+        studylevel.level_session = 2010
+        self.student['studycourse']['200'] = studylevel
+        result = get_students('fac1', 'dep1', 2010)
+        self.assertEqual(result,
+            [[], [], [], [], [], [], [(u'K1000000', u'1234', u'Bob Tester')]])
+        return
+
+    def test_get_students_without_level_in_session(self):
+        self.student['studycourse']['100'].level_session = 2011
+        result = get_students('fac1', 'dep1', 2010)
+        self.assertEqual(result,
+            [[], [], [], [], [], [], []])
+        return
+
+    def test_create_pdf(self):
+        report = SessionResultsPresentation('fac1', 'dep1', 2010, 100)
+        result = report.create_pdf()
+        self.assertTrue(result.startswith('%PDF-'))
+        path = os.path.join(samples_dir(), 'session_results_presentation.pdf')
+        open(path, 'wb').write(result)
+        print "Sample session_results_presentation.pdf written to %s" % path
+        return
+
+class SessionResultsPresentationUITests(StudentsFullSetup, FunctionalAsyncTestCase):
+
+    layer = FunctionalLayer
+
+    def wait_for_report_job_completed(self):
+        # helper function waiting until the current export job is completed
+        manager = getUtility(IJobManager)
+        job_id = self.app['reports'].running_report_jobs[0][0]
+        job = manager.get(job_id)
+        wait_for_result(job)
+        return job_id
+
+    def stored_in_reports(self, job_id):
+        # tell whether job_id is stored in reports's running jobs list
+        for entry in list(self.app['reports'].running_report_jobs):
+            if entry[0] == job_id:
+                return True
+        return False
+
+    def trigger_report_creation(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open('http://localhost/app/reports')
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.browser.getLink("Create new report").click()
+        self.browser.getControl(name="generator").value = ['session_results_presentation']
+        self.browser.getControl("Configure").click()
+        self.browser.getControl(name="level").value = ['100']
+        self.browser.getControl(name="session").value = ['2010']
+        self.browser.getControl(name="faccode_depcode").value = ['fac1_dep1']
+        self.browser.getControl("Create").click()
+        return
+
+    def test_report_download(self):
+        # We can download a generated report
+        self.trigger_report_creation()
+        # When the job is finished and we reload the page...
+        job_id = self.wait_for_report_job_completed()
+        self.browser.open('http://localhost/app/reports')
+        # ... the pdf file can be downloaded ...
+        self.browser.getControl("Download").click()
+        self.assertEqual(self.browser.headers['content-type'],
+                         'application/pdf')
+        self.assertTrue(
+            'filename="SessionResultsPresentation_fac1_dep1_2010_100_' in
+            self.browser.headers['content-disposition'])
+        self.assertEqual(len(self.app['reports'].running_report_jobs), 1)
+        job_id = self.app['reports'].running_report_jobs[0][0]
+        # ... and discarded
+        self.browser.open('http://localhost/app/reports')
+        self.browser.getControl("Discard").click()
+        self.assertEqual(len(self.app['reports'].running_report_jobs), 0)
+        # Creation, downloading and discarding is logged
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'main.log')
+        logcontent = open(logfile).read()
+        self.assertTrue(
+            'INFO - zope.mgr - students.reports.session_results_presentation.SessionResultsPresentationGeneratorPage - '
+            'report %s created: Session Results Presentation (faculty=fac1, department=dep1, session=2010, level=100)'
+            % job_id in logcontent
+            )
+        self.assertTrue(
+            'INFO - zope.mgr - students.reports.session_results_presentation.SessionResultsPresentationPDFView - '
+            'report %s downloaded: SessionResultsPresentation_fac1_dep1_2010_100_'
+            % job_id in logcontent
+            )
+        self.assertTrue(
+            'INFO - zope.mgr - browser.reports.ReportsContainerPage - '
+            'report %s discarded' % job_id in logcontent
+            )
+        return
