Index: main/waeup.kofa/trunk/docs/source/userdocs/students.rst
===================================================================
--- main/waeup.kofa/trunk/docs/source/userdocs/students.rst	(revision 13001)
+++ main/waeup.kofa/trunk/docs/source/userdocs/students.rst	(revision 13002)
@@ -118,5 +118,7 @@
    :pyobject: IStudentStudyCourse
 
-The first schema field is a `Choice` field and only values from the `CertificateSource` are allowed. The source provides all certificates stored in the portal. This way, `StudentStudyCourse` objects point to exactly one `Certificate` object in the portal. If the certificate is removed, an event handler takes care of clearing also all referring `certificate` attribute. The tokens of the `CertificateSource` are the certicate codes which means, that in forms and import files the unique code must be given and the desired `Certificate` object will be stored.
+All attributes are read-only property attributes which are computed dynamically.
+
+The first schema field is a `Choice` field which allows values from the `CertificateSource`. The source provides all certificates stored in the portal. This way, `StudentStudyCourse` objects point to exactly one `Certificate` object in the portal. If the certificate is removed, an event handler takes care of clearing also all referring `certificate` attribute. The tokens of the `CertificateSource` are the certicate codes which means, that in forms and import files the unique code must be given and the desired `Certificate` object will be stored.
 
 The interface has two entry parameter fields. `entry_mode` stores the study mode of the programme and `entry_session` the academic year when the study course was started. Usually these parameters are fixed and must not be changed during course of study.
@@ -126,11 +128,17 @@
 What is a verdict? A verdict is the individual judgement of a jury (senate in Nigeria) at the end of each academic session. The jury's verdict tells the portal, if the student has successfully completed an academic session or not. Depending on the verdict, the student will be either allowed to proceed to the next study level or forced to repeat the current level. Without a verdict, the student gets stuck and cannot even pay school fees for the next academic year. This will be further exlplained in the workflow section below.
 
-`StudentStudyCourse` is a container class. Study courses contain `StudentStudyLevel` instances.
+`StudentStudyCourse` is a container class. Study courses contain `StudentStudyLevel` instances which implement `waeup.kofa.students.interfaces.IStudentStudyLevel`.
 
 `IStudentStudyLevel`
 --------------------
 
+`StudentStudyLevel` instances contain information about the study progress achieved at that level. In Kofa study levels range from 100 to 900. There are two additional levels: a pre-studies level (10) and a special postgraduate level (999). There are also two probation levels per regular study level. Level 120, for instance, means level 100 on second probation. The complete numbering of study levels can be viewed `here <https://kofa-demo.waeup.org/sources#collapseStudyLevels>`_.
+
+`StudentStudyLevel` instances are container objects which contain course tickets. We therefore sometimes speak of a level course list instead of a study level. The study level stores and provides the information when (`validation_date`) and by whom (`validated_by`) the course list was validated, in which session the level was taken (`level_session`) and which verdict the student finally obtained.
+
 .. literalinclude:: ../../../src/waeup/kofa/students/interfaces.py
    :pyobject: IStudentStudyLevel
+
+`StudentStudyLevel` instances also compute some statistical values on the fly to give an overview of the courses taken. These read-only property attributes are: `number_of_tickets`, `passed_params`, `gpa_params`, `gpa_params_rectified`, `cumulative_params`, `total_credits` and `gpa`. The attentive reader may wonder why the latter two are not listed in the attributes section of the interface, but as read-only schema fields further below. This is a trick which we used to properly display these fields on some form pages and pdf slips. However, the `attrs_to_fields` function explicitly excludes them when starting the portal, so that these fields are not used for persistent storage of same-named attributes in the database.
 
 `ICourseTicket`
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 13001)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py	(revision 13002)
@@ -427,4 +427,10 @@
     """Representation of student study course data.
     """
+    student = Attribute('Student object')
+    next_session_allowed = Attribute('True if the student can proceed to next session')
+    is_postgrad = Attribute('True if student is postgraduate student')
+    is_current = Attribute('True if the study course is the current course of studies')
+    is_previous = Attribute('True if the study course is the previous course of studies')
+
     certificate = schema.Choice(
         title = _(u'Certificate'),
@@ -476,4 +482,17 @@
         )
 
+    def writeLogMessage(view, message):
+        """Write formatted log message into students.log.
+        """
+
+    def addStudentStudyLevel(cert, studylevel):
+        """Add a study level object.
+        """
+
+    def getTranscriptData():
+        """Get a sorted list of dicts with level and course ticket data.
+        This method is used for transcripts.
+        """
+
 class IStudentStudyCourseTransfer(IStudentStudyCourse):
     """An interface used for student transfers.
@@ -558,8 +577,15 @@
     """A representation of student study level data. 
     """
+    student = Attribute('Student object')
+
+    certcode = Attribute('The certificate code of the study course')
+    is_current_level = Attribute('True if level is current level of the student')
+    level_title = Attribute('Level title from source')
+    getSessionString = Attribute('Session title from source')
     number_of_tickets = Attribute('Number of tickets contained in this level')
-    certcode = Attribute('The certificate code of the study course')
-    is_current_level = Attribute('Is this level the current level of the student?')
     passed_params = Attribute('Information about passed and failed courses')
+    gpa_params_rectified = Attribute('Corrected sessional GPA parameters')
+    gpa_params = Attribute('GPA parameters for this level.')
+    cumulative_params = Attribute('Cumulative GPA and other cumulative parameters for this level')
 
     level = schema.Choice(
@@ -606,4 +632,8 @@
         readonly = True,
         )
+
+    def writeLogMessage(view, message):
+        """Write formatted log message into students.log.
+        """
 
     def addCourseTicket(ticket, course):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/studycourse.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/studycourse.py	(revision 13001)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/studycourse.py	(revision 13002)
@@ -71,5 +71,5 @@
     @property
     def is_current(self):
-        if '_' in self.__name__:
+        if self.__name__ and '_' in self.__name__:
             return False
         return True
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py	(revision 13001)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py	(revision 13002)
@@ -101,6 +101,5 @@
     @property
     def gpa_params_rectified(self):
-        """Calculate corrected corrected level (sesional) gpa parameters.
-
+        """Calculate corrected level (sessional) gpa parameters.
         The corrected gpa is displayed on transcripts only.
         """
@@ -166,5 +165,4 @@
         """Calculate the cumulative gpa and other cumulative parameters
         for this level.
-
         All levels below this level are taken under consideration
         (including repeating levels). This method is used for level reports.
@@ -175,14 +173,15 @@
         total_credits_weighted = 0
         cgpa = 0.0
-        for level in self.__parent__.values():
-            if level.level > self.level:
-                continue
-            credits_passed += level.passed_params[2]
-            total_credits += level.total_credits
-            gpa_params = level.gpa_params
-            total_credits_counted += gpa_params[1]
-            total_credits_weighted += gpa_params[2]
-        if total_credits_counted:
-            cgpa = round(total_credits_weighted / total_credits_counted, 3)
+        if self.__parent__:
+            for level in self.__parent__.values():
+                if level.level > self.level:
+                    continue
+                credits_passed += level.passed_params[2]
+                total_credits += level.total_credits
+                gpa_params = level.gpa_params
+                total_credits_counted += gpa_params[1]
+                total_credits_weighted += gpa_params[2]
+            if total_credits_counted:
+                cgpa = round(total_credits_weighted / total_credits_counted, 3)
         return (cgpa, total_credits_counted, total_credits_weighted,
                total_credits, credits_passed)
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_student.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_student.py	(revision 13001)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_student.py	(revision 13002)
@@ -78,4 +78,5 @@
         self.studycourse = StudentStudyCourse()
         self.studylevel = StudentStudyLevel()
+        self.studylevel.level_session = 2015
         self.courseticket = CourseTicket()
         self.payments = StudentPaymentsContainer()
