Index: main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py	(revision 10596)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/studylevel.py	(revision 10598)
@@ -102,5 +102,9 @@
     @property
     def gpa_params_rectified(self):
-        weighted_credits = 0.0
+        """Calculate corrected corrected level (sesional) gpa parameters.
+
+        The corrected gpa is displayed on transcripts only.
+        """
+        credits_weighted = 0.0
         credits_counted = 0
         level_gpa = 0.0
@@ -111,15 +115,17 @@
                     if co_ticket is not None and co_ticket.weight is not None:
                         credits_counted += co_ticket.credits
-                        weighted_credits += co_ticket.credits * co_ticket.weight
+                        credits_weighted += co_ticket.credits * co_ticket.weight
                     continue
                 credits_counted += ticket.credits
-                weighted_credits += ticket.credits * ticket.weight
+                credits_weighted += ticket.credits * ticket.weight
         if credits_counted:
-            level_gpa = round(weighted_credits/credits_counted, 3)
-        return level_gpa, credits_counted, weighted_credits
+            level_gpa = round(credits_weighted/credits_counted, 3)
+        return level_gpa, credits_counted, credits_weighted
 
     @property
     def gpa_params(self):
-        weighted_credits = 0.0
+        """Calculate gpa parameters for this level.
+        """
+        credits_weighted = 0.0
         credits_counted = 0
         level_gpa = 0.0
@@ -127,8 +133,8 @@
             if ticket.score is not None:
                 credits_counted += ticket.credits
-                weighted_credits += ticket.credits * ticket.weight
+                credits_weighted += ticket.credits * ticket.weight
         if credits_counted:
-            level_gpa = round(weighted_credits/credits_counted, 3)
-        return level_gpa, credits_counted, weighted_credits
+            level_gpa = round(credits_weighted/credits_counted, 3)
+        return level_gpa, credits_counted, credits_weighted
 
     @property
@@ -138,4 +144,8 @@
     @property
     def passed_params(self):
+        """Determine the number and credits of passed and failed courses.
+
+        This method is used for level reports.
+        """
         passed = failed = 0
         failed_courses = []
@@ -154,4 +164,30 @@
 
     @property
+    def cumulative_params(self):
+        """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.
+        """
+        passed_credits = 0
+        total_credits = 0
+        total_credits_counted = 0
+        total_credits_weighted = 0
+        cgpa = 0
+        for level in self.__parent__.values():
+            if level.level > self.level:
+                continue
+            passed_credits += 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, passed_credits)
+
+    @property
     def is_current_level(self):
         try:
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 10596)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 10598)
@@ -814,5 +814,5 @@
         self.assertMatches(
             '...: Study Level 200 (Year 2)...', self.browser.contents)
-        # If COURSE1 has score 10 it becomes a carry-over course
+        # Since COURSE1 has score 10 it becomes a carry-over course
         # in level 200
         self.assertEqual(
@@ -827,4 +827,15 @@
             self.student['studycourse']['200'].passed_params,
             (0, 0, 0, 0, []))
+        # And also cumulative params can be calculated. Meanwhile we have the
+        # following courses: COURSE1 and COURSE2 in level 100 and
+        # COURSE1 as carry-over course in level 200.
+        self.assertEqual(
+            self.student['studycourse']['100'].cumulative_params,
+            (2.261, 23, 52.0, 23, 13))
+        # COURSE1 in level 200 is not taken into consideration
+        # when calculating the gpa.
+        self.assertEqual(
+            self.student['studycourse']['200'].cumulative_params,
+            (2.261, 23, 52.0, 33, 13))
         return
 
