MDL-47059 Grades: Make natural weights work with exclude empty grades
authorDamyon Wiese <damyon@moodle.com>
Thu, 18 Sep 2014 08:53:08 +0000 (16:53 +0800)
committerAdrian Greeve <adrian@moodle.com>
Fri, 3 Oct 2014 05:53:01 +0000 (13:53 +0800)
Part of: MDL-46576

grade/tests/behat/grade_aggregation.feature
lib/grade/grade_category.php

index 16ebe18..90aa91b 100644 (file)
@@ -296,15 +296,25 @@ Feature: We can use calculated grade totals
   Scenario: Natural aggregation
     And I follow "Edit   Sub category 1"
     And I set the field "Aggregation" to "Natural"
+    And I click on "Show more..." "link"
+    And I set the field "Exclude empty grades" to "0"
     And I press "Save changes"
     And I follow "Edit   Sub category 2"
     And I set the field "Aggregation" to "Natural"
+    And I click on "Show more..." "link"
+    And I set the field "Exclude empty grades" to "1"
     And I press "Save changes"
     And I follow "Edit   Course 1"
     And I set the field "Aggregation" to "Natural"
+    And I click on "Show more..." "link"
+    And I set the field "Exclude empty grades" to "0"
+    And I press "Save changes"
+    And I click on "Edit  assign Test assignment six" "link"
+    And I set the field "Weight adjusted" to "1"
+    And I set the field "aggregationcoef2" to "0.5"
     And I press "Save changes"
     And I turn editing mode off
-    Then I should see "150.00 (18.99 %)" in the ".course" "css_element"
+    Then I should see "152.68 (19.70 %)" in the ".course" "css_element"
     And I follow "Course grade settings"
     And I set the field "Hide totals if they contain hidden items" to "Show totals excluding hidden items"
     And I press "Save changes"
@@ -313,4 +323,4 @@ Feature: We can use calculated grade totals
     And I follow "Course 1"
     And I follow "Grades"
     And I set the field "Grade report" to "Overview report"
-    And I should see "110.00 (13.92 %)" in the "overview-grade" "table"
+    And I should see "113.75 (17.91 %)" in the "overview-grade" "table"
index 2988518..1256e30 100644 (file)
@@ -965,12 +965,11 @@ class grade_category extends grade_object {
                 $num = count($grade_values);
                 $sum = 0;
                 $sumweights = 0;
-                $weightsfix = 1;
                 $grademin = 0;
                 $grademax = 0;
                 foreach ($grade_values as $itemid => $gradevalue) {
                     // We need to check if the grademax/min was adjusted per user because of excluded items.
-                    $usergrademin = $items[$itemid]->grademax;
+                    $usergrademin = $items[$itemid]->grademin;
                     $usergrademax = $items[$itemid]->grademax;
                     if (isset($grademinoverrides[$itemid])) {
                         $usergrademin = $grademinoverrides[$itemid];
@@ -978,7 +977,7 @@ class grade_category extends grade_object {
                     if (isset($grademaxoverrides[$itemid])) {
                         $usergrademax = $grademaxoverrides[$itemid];
                     }
-                    $gradeitemrange = $usergrademin - $usergrademax;
+                    $gradeitemrange = $usergrademax - $usergrademin;
 
                     // Extra credit.
                     if (!empty($items[$itemid]->aggregationcoef)) {
@@ -986,13 +985,44 @@ class grade_category extends grade_object {
                         $sumweights += $items[$itemid]->aggregationcoef2;
                     }
                 }
-                if ($sumweights != 1 && $sumweights != 0) {
-                    $weightsfix = 1 / $sumweights;
+                $userweights = array();
+                $totaloverriddenweight = 0;
+                $totaloverriddengrademax = 0;
+                // We first need to rescale all manually assigned weights down by the
+                // percentage of weights missing from the category.
+                foreach ($grade_values as $itemid => $gradevalue) {
+                    if ($items[$itemid]->weightoverride) {
+                        $userweights[$itemid] = $items[$itemid]->aggregationcoef2 / $sumweights;
+                        $totaloverriddenweight += $userweights[$itemid];
+                        $usergrademax = $items[$itemid]->grademax;
+                        if (isset($grademaxoverrides[$itemid])) {
+                            $usergrademax = $grademaxoverrides[$itemid];
+                        }
+                        $totaloverriddengrademax += $usergrademax;
+                    }
+                }
+                $nonoverriddenpoints = $grademax - $totaloverriddengrademax;
+
+                // Then we need to recalculate the automatic weights.
+                foreach ($grade_values as $itemid => $gradevalue) {
+                    if (!$items[$itemid]->weightoverride) {
+                        if ($nonoverriddenpoints > 0) {
+                            $usergrademax = $items[$itemid]->grademax;
+                            if (isset($grademaxoverrides[$itemid])) {
+                                $usergrademax = $grademaxoverrides[$itemid];
+                            }
+                            $userweights[$itemid] = ($usergrademax/$nonoverriddenpoints) * (1 - $totaloverriddenweight);
+                        } else {
+                            $userweights[$itemid] = 0;
+                        }
+                    }
                 }
+
+                // We can use our freshly corrected weights below.
                 foreach ($grade_values as $itemid => $gradevalue) {
-                    $sum += $gradevalue * $items[$itemid]->aggregationcoef2 * $grademax * $weightsfix;
+                    $sum += $gradevalue * $userweights[$itemid] * $grademax;
                     if ($weights !== null) {
-                        $weights[$itemid] = $items[$itemid]->aggregationcoef2 * $weightsfix;
+                        $weights[$itemid] = $userweights[$itemid];
                     }
                 }
                 if ($grademax > 0) {