Merge branch 'MDL-47243-master' of git://github.com/xow/moodle
authorDan Poltawski <dan@moodle.com>
Wed, 15 Oct 2014 15:14:39 +0000 (16:14 +0100)
committerDan Poltawski <dan@moodle.com>
Wed, 15 Oct 2014 15:14:39 +0000 (16:14 +0100)
admin/tool/behat/tests/behat/data_generators.feature
grade/edit/tree/outcomeitem.php
grade/edit/tree/outcomeitem_form.php
grade/report/grader/lib.php
grade/tests/behat/grade_aggregation.feature
lib/testing/generator/data_generator.php
lib/tests/behat/behat_data_generators.php

index c9758f2..2b32cb0 100644 (file)
@@ -185,6 +185,12 @@ Feature: Set up contextual data for tests
       | url        | Test url name          | Test url description          | C1     | url1        |
       | wiki       | Test wiki name         | Test wiki description         | C1     | wiki1       |
       | workshop   | Test workshop name     | Test workshop description     | C1     | workshop1   |
+    And the following "scales" exist:
+      | name | scale |
+      | Test Scale 1 | Disappointing, Good, Very good, Excellent |
+    And the following "activities" exist:
+      | activity   | name                            | intro                         | course | idnumber    | grade |
+      | assign     | Test assignment name with scale | Test assignment description   | C1     | assign1     | Test Scale 1 |
     When I log in as "admin"
     And I follow "Course 1"
     Then I should see "Test assignment name"
@@ -214,6 +220,10 @@ Feature: Set up contextual data for tests
     And I should see "Test workshop name"
     And I follow "Test assignment name"
     And I should see "Test assignment description"
+    And I follow "C1"
+    And I follow "Test assignment name with scale"
+    And I follow "Edit settings"
+    And the field "Type" matches value "Scale"
 
   @javascript
   Scenario: Add relations between users and groups
@@ -297,13 +307,124 @@ Feature: Set up contextual data for tests
       | Course 1 | C1 |
     And the following "grade categories" exist:
       | fullname | course |
-      | Grade category 1 | C1|
+      | Grade category 1 | C1 |
     And the following "grade categories" exist:
       | fullname | course | gradecategory |
-      | Grade sub category 2 | C1 | Grade category 1|
+      | Grade sub category 2 | C1 | Grade category 1 |
     When I log in as "admin"
     And I follow "Courses"
     And I follow "Course 1"
     And I navigate to "Grades" node in "Course administration"
     Then I should see "Grade category 1"
     And I should see "Grade sub category 2"
+
+  Scenario: Add a bunch of grade items
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1 |
+    And the following "grade categories" exist:
+      | fullname | course |
+      | Grade category 1 | C1 |
+    And the following "grade categories" exist:
+      | fullname | course | gradecategory |
+      | Grade sub category 2 | C1 | Grade category 1 |
+    And the following "grade items" exist:
+      | itemname    | course |
+      | Test Grade Item 1 | C1 |
+    And the following "grade items" exist:
+      | itemname    | course | gradecategory |
+      | Test Grade Item 2 | C1 | Grade category 1 |
+      | Test Grade Item 3 | C1 | Grade sub category 2 |
+    When I log in as "admin"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I expand "Setup" node
+    And I follow "Categories and items"
+    Then I should see "Test Grade Item 1"
+    And I follow "Edit   Test Grade Item 1"
+    And I expand all fieldsets
+    And I should see "Course 1"
+    And I press "Cancel"
+    And I should see "Grade category 1"
+    And I should see "Test Grade Item 2"
+    And I follow "Edit   Test Grade Item 2"
+    And I expand all fieldsets
+    And I should see "Grade category 1"
+    And I press "Cancel"
+    And I should see "Grade sub category 2"
+    And I should see "Test Grade Item 3"
+    And I follow "Edit   Test Grade Item 3"
+    And I expand all fieldsets
+    And I should see "Grade sub category 2"
+    And I press "Cancel"
+
+  Scenario: Add a bunch of scales
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1 |
+    And the following "scales" exist:
+      | name | scale |
+      | Test Scale 1 | Disappointing, Good, Very good, Excellent |
+    When I log in as "admin"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I follow "Scales"
+    Then I should see "Test Scale 1"
+    And I should see "Disappointing,  Good,  Very good,  Excellent"
+
+  Scenario: Add a bunch of outcomes
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1        |
+    And the following "scales" exist:
+      | name | scale |
+      | Test Scale 1 | Disappointing, Good, Very good, Excellent |
+    And the following "grade outcomes" exist:
+      | fullname        | shortname | scale        |
+      | Grade outcome 1 | OT1       | Test Scale 1 |
+    And the following "grade outcomes" exist:
+      | fullname        | shortname | course | scale        |
+      | Grade outcome 2 | OT2       | C1     | Test Scale 1 |
+    When I log in as "admin"
+    And I set the following administration settings values:
+      | Enable outcomes | 1 |
+    And I follow "Home"
+    And I follow "Course 1"
+    And I follow "Outcomes"
+    Then I should see "Grade outcome 1" in the "#addoutcomes" "css_element"
+    And I should see "Grade outcome 2" in the "#removeoutcomes" "css_element"
+    And I follow "Edit outcomes"
+    And the following should exist in the "generaltable" table:
+      | Full name       | Short name | Scale        |
+      | Grade outcome 2 | OT2        | Test Scale 1 |
+
+  Scenario: Add a bunch of outcome grade items
+    Given the following "courses" exist:
+      | fullname | shortname |
+      | Course 1 | C1        |
+    And the following "scales" exist:
+      | name         | scale                                     |
+      | Test Scale 1 | Disappointing, Good, Very good, Excellent |
+    And the following "grade outcomes" exist:
+      | fullname        | shortname | course | scale        |
+      | Grade outcome 1 | OT1       | C1     | Test Scale 1 |
+    And the following "grade categories" exist:
+      | fullname         | course |
+      | Grade category 1 | C1     |
+     And the following "grade items" exist:
+       | itemname                  | course | outcome | gradecategory    |
+       | Test Outcome Grade Item 1 | C1     | OT1     | Grade category 1 |
+    When I log in as "admin"
+    And I set the following administration settings values:
+      | Enable outcomes | 1 |
+    And I follow "Home"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I expand "Setup" node
+    And I follow "Categories and items"
+    Then I should see "Test Outcome Grade Item 1"
+    And I follow "Edit   Test Outcome Grade Item 1"
+    And the field "Outcome" matches value "Grade outcome 1"
+    And I expand all fieldsets
+    And "//div[contains(@class, 'fitem')]/div[contains(@class, 'fitemtitle')]/div[contains(@class, fstaticlabel) and contains(., 'Grade category')]/../../div[contains(@class, 'felement') and contains(., 'Grade category 1')]" "xpath_element" should exist
+    And I press "Cancel"
index 6c5c330..52bc618 100644 (file)
@@ -104,6 +104,7 @@ if (empty($parent_category)) {
     $item->aggregationcoef = 0;
 } else if ($parent_category->aggregation == GRADE_AGGREGATE_SUM) {
     $item->aggregationcoef = $item->aggregationcoef > 0 ? 1 : 0;
+    $item->aggregationcoef2 = format_float($item->aggregationcoef2 * 100.0);
 } else {
     $item->aggregationcoef = format_float($item->aggregationcoef, 4);
 }
@@ -131,12 +132,15 @@ if ($data = $mform->get_data()) {
     unset($data->locked);
     unset($data->locktime);
 
-    $convert = array('gradepass', 'aggregationcoef');
+    $convert = array('gradepass', 'aggregationcoef', 'aggregationcoef2');
     foreach ($convert as $param) {
         if (property_exists($data, $param)) {
             $data->$param = unformat_float($data->$param);
         }
     }
+    if (isset($data->aggregationcoef2) && $parent_category->aggregation == GRADE_AGGREGATE_SUM) {
+        $data->aggregationcoef2 = $data->aggregationcoef2 / 100.0;
+    }
 
     $grade_item = new grade_item(array('id'=>$id, 'courseid'=>$courseid));
     grade_item::set_properties($grade_item, $data);
index 8ae803f..9428e5b 100644 (file)
@@ -73,6 +73,14 @@ class edit_outcomeitem_form extends moodleform {
         $mform->addHelpButton('cmid', 'linkedactivity', 'grades');
         $mform->setDefault('cmid', 0);
 
+        $mform->addElement('advcheckbox', 'weightoverride', get_string('adjustedweight', 'grades'));
+        $mform->addHelpButton('weightoverride', 'weightoverride', 'grades');
+
+        $mform->addElement('text', 'aggregationcoef2', get_string('weight', 'grades'));
+        $mform->addHelpButton('aggregationcoef2', 'weight', 'grades');
+        $mform->setType('aggregationcoef2', PARAM_RAW);
+        $mform->disabledIf('aggregationcoef2', 'weightoverride');
+
         /// hiding
         /// advcheckbox is not compatible with disabledIf !!
         $mform->addElement('checkbox', 'hidden', get_string('hidden', 'grades'));
@@ -125,8 +133,9 @@ class edit_outcomeitem_form extends moodleform {
         }
 
         if ($coefstring !== '') {
-            if ($coefstring == 'aggregationcoefextrasum') {
+            if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') {
                 // advcheckbox is not compatible with disabledIf!
+                $coefstring = 'aggregationcoefextrasum';
                 $mform->addElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades'));
             } else {
                 $mform->addElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
@@ -193,7 +202,7 @@ class edit_outcomeitem_form extends moodleform {
 
                 $parent_category->apply_forced_settings();
 
-                if (!$parent_category->is_aggregationcoef_used() or $parent_category->aggregation == GRADE_AGGREGATE_SUM) {
+                if (!$parent_category->is_aggregationcoef_used()) {
                     if ($mform->elementExists('aggregationcoef')) {
                         $mform->removeElement('aggregationcoef');
                     }
@@ -219,6 +228,15 @@ class edit_outcomeitem_form extends moodleform {
                         $mform->addHelpButton('aggregationcoef', $aggcoef, 'grades');
                     }
                 }
+                // Remove fields used by natural weighting if the parent category is not using natural weighting.
+                if ($parent_category->aggregation != GRADE_AGGREGATE_SUM) {
+                    if ($mform->elementExists('weightoverride')) {
+                        $mform->removeElement('weightoverride');
+                    }
+                    if ($mform->elementExists('aggregationcoef2')) {
+                        $mform->removeElement('aggregationcoef2');
+                    }
+                }
             }
 
         }
index aa4a8ed..32577b8 100644 (file)
@@ -742,7 +742,6 @@ class grade_report_grader extends grade_report {
         $strftimedatetimeshort = get_string('strftimedatetimeshort');
         $strexcludedgrades = get_string('excluded', 'grades');
         $strerror = get_string('error');
-        $strtypescale = get_string('typescale', 'grades');
 
         foreach ($this->gtree->get_levels() as $key => $row) {
             $headingrow = new html_table_row();
@@ -991,7 +990,9 @@ class grade_report_grader extends grade_report {
                                 $nogradestr = $this->get_lang_string('nooutcome', 'grades');
                             }
                             $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id'=>'grade_'.$userid.'_'.$item->id);
-                            $itemcell->text .= html_writer::label($strtypescale, $attributes['id'], false,
+                            $gradelabel = $fullname . ' ' . $item->itemname;
+                            $itemcell->text .= html_writer::label(
+                                get_string('useractivitygrade', 'gradereport_grader', $gradelabel), $attributes['id'], false,
                                     array('class' => 'accesshide'));
                             $itemcell->text .= html_writer::select($scaleopt, 'grade['.$userid.']['.$item->id.']', $gradeval, array(-1=>$nogradestr), $attributes);
                         } else if (!empty($scale)) {
index 6dffd30..40e7063 100644 (file)
@@ -246,6 +246,126 @@ Feature: We can use calculated grade totals
     And I set the field "Grade report" to "Overview report"
     And I should see "50.00 (50.00 %)" in the "overview-grade" "table"
 
+  @javascript
+  Scenario: Natural aggregation on outcome items with natural weights
+    And I log out
+    And I log in as "admin"
+    And I set the following administration settings values:
+      | Enable outcomes | 1 |
+    And the following "scales" exist:
+      | name       | scale                                     |
+      | Test Scale | Disappointing, Good, Very good, Excellent |
+    And the following "grade outcomes" exist:
+      | fullname  | shortname | course | scale      |
+      | Outcome 1 | OT1       | C1     | Test Scale |
+    And the following "grade items" exist:
+      | itemname              | course | outcome | gradetype | scale      |
+      | Test outcome item one | C1     | OT1     | Scale     | Test Scale |
+    And I log out
+    When I log in as "teacher1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I expand "Setup" node
+    And I follow "Categories and items"
+    And I set the following settings for grade item "Course 1":
+      | Aggregation                     | Natural |
+      | Include outcomes in aggregation | 1       |
+      | Exclude empty grades            | 0       |
+    And I follow "Grader report"
+    And I turn editing mode on
+    And I press "Save changes"
+    And I give the grade "Excellent" to the user "Student 1" for the grade item "Test outcome item one"
+    And I press "Save changes"
+    And I navigate to "Course grade settings" node in "Grade administration > Setup"
+    And I set the field "report_overview_showtotalsifcontainhidden" to "Show totals excluding hidden items"
+    And I set the field "report_user_showtotalsifcontainhidden" to "Show totals excluding hidden items"
+    And I press "Save changes"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    Then "Test outcome item one" row "Grade" column of "user-grade" table should contain "Excellent (100.00 %)"
+    And I set the field "Grade report" to "Overview report"
+    And I should see "114.82 (18.27 %)" in the "overview-grade" "table"
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I expand "Setup" node
+    And I follow "Categories and items"
+    And I set the following settings for grade item "Test outcome item one":
+     | Extra credit     | 1   |
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    Then "Test outcome item one" row "Grade" column of "user-grade" table should contain "Excellent (100.00 %)"
+    And I set the field "Grade report" to "Overview report"
+    And I should see "114.00 (18.39 %)" in the "overview-grade" "table"
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I expand "Setup" node
+    And I follow "Categories and items"
+    And I set the following settings for grade item "Course 1":
+      | Aggregation                     | Natural |
+      | Include outcomes in aggregation | 0       |
+    And I set the following settings for grade item "Test outcome item one":
+     | Extra credit     | 0   |
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    Then "Test outcome item one" row "Grade" column of "user-grade" table should contain "Excellent (100.00 %)"
+    And I set the field "Grade report" to "Overview report"
+    And I should see "110.00 (17.74 %)" in the "overview-grade" "table"
+
+  @javascript
+  Scenario: Natural aggregation on outcome items with modified weights
+    And I log out
+    And I log in as "admin"
+    And I set the following administration settings values:
+      | Enable outcomes | 1 |
+    And the following "scales" exist:
+      | name       | scale                                     |
+      | Test Scale | Disappointing, Good, Very good, Excellent |
+    And the following "grade outcomes" exist:
+      | fullname  | shortname | course | scale      |
+      | Outcome 1 | OT1       | C1     | Test Scale |
+    And the following "grade items" exist:
+      | itemname              | course | outcome | gradetype | scale      |
+      | Test outcome item one | C1     | OT1     | Scale     | Test Scale |
+    And I log out
+    When I log in as "teacher1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    And I expand "Setup" node
+    And I follow "Categories and items"
+    And I set the following settings for grade item "Course 1":
+      | Aggregation                     | Natural |
+      | Include outcomes in aggregation | 1       |
+      | Exclude empty grades            | 0       |
+    And I set the following settings for grade item "Test outcome item one":
+     | Weight adjusted  | 1   |
+     | aggregationcoef2 | 100 |
+    And I follow "Grader report"
+    And I turn editing mode on
+    And I press "Save changes"
+    And I give the grade "Excellent" to the user "Student 1" for the grade item "Test outcome item one"
+    And I press "Save changes"
+    And I navigate to "Course grade settings" node in "Grade administration > Setup"
+    And I set the field "report_overview_showtotalsifcontainhidden" to "Show totals excluding hidden items"
+    And I set the field "report_user_showtotalsifcontainhidden" to "Show totals excluding hidden items"
+    And I press "Save changes"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Grades"
+    Then "Test outcome item one" row "Grade" column of "user-grade" table should contain "Excellent (100.00 %)"
+    And I set the field "Grade report" to "Overview report"
+    And I should see "4.00 (100.00 %)" in the "overview-grade" "table"
+
   @javascript
   Scenario: Natural aggregation
     And I set the following settings for grade item "Sub category 1":
index 55ef5df..2bca043 100644 (file)
@@ -34,7 +34,12 @@ defined('MOODLE_INTERNAL') || die();
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class testing_data_generator {
+    /** @var int The number of grade categories created */
     protected $gradecategorycounter = 0;
+    /** @var int The number of grade items created */
+    protected $gradeitemcounter = 0;
+    /** @var int The number of grade outcomes created */
+    protected $gradeoutcomecounter = 0;
     protected $usercounter = 0;
     protected $categorycount = 0;
     protected $cohortcount = 0;
@@ -914,7 +919,6 @@ EOD;
         global $CFG;
 
         $this->gradecategorycounter++;
-        $i = $this->gradecategorycounter;
 
         $record = (array)$record;
 
@@ -923,7 +927,7 @@ EOD;
         }
 
         if (!isset($record['fullname'])) {
-            $record['fullname'] = 'Grade category ' . $i;
+            $record['fullname'] = 'Grade category ' . $this->gradecategorycounter;
         }
 
         // For gradelib classes.
@@ -941,4 +945,68 @@ EOD;
         $gradecategory->update_from_db();
         return $gradecategory->get_record_data();
     }
+
+    /**
+     * Create a grade_item.
+     *
+     * @param array|stdClass $record
+     * @return stdClass the grade item record
+     */
+    public function create_grade_item($record = null) {
+        global $CFG;
+        require_once("$CFG->libdir/gradelib.php");
+
+        $this->gradeitemcounter++;
+
+        if (!isset($record['itemtype'])) {
+            $record['itemtype'] = 'manual';
+        }
+
+        if (!isset($record['itemname'])) {
+            $record['itemname'] = 'Grade item ' . $this->gradeitemcounter;
+        }
+
+        if (isset($record['outcomeid'])) {
+            $outcome = new grade_outcome(array('id' => $record['outcomeid']));
+            $record['scaleid'] = $outcome->scaleid;
+        }
+        if (isset($record['scaleid'])) {
+            $record['gradetype'] = GRADE_TYPE_SCALE;
+        } else if (!isset($record['gradetype'])) {
+            $record['gradetype'] = GRADE_TYPE_VALUE;
+        }
+
+        // Create new grade item in this course.
+        $gradeitem = new grade_item($record, false);
+        $gradeitem->insert();
+
+        $gradeitem->update_from_db();
+        return $gradeitem->get_record_data();
+    }
+
+    /**
+     * Create a grade_outcome.
+     *
+     * @param array|stdClass $record
+     * @return stdClass the grade outcome record
+     */
+    public function create_grade_outcome($record = null) {
+        global $CFG;
+
+        $this->gradeoutcomecounter++;
+        $i = $this->gradeoutcomecounter;
+
+        if (!isset($record['fullname'])) {
+            $record['fullname'] = 'Grade outcome ' . $i;
+        }
+
+        // For gradelib classes.
+        require_once($CFG->libdir . '/gradelib.php');
+        // Create new grading outcome in this course.
+        $gradeoutcome = new grade_outcome($record, false);
+        $gradeoutcome->insert();
+
+        $gradeoutcome->update_from_db();
+        return $gradeoutcome->get_record_data();
+    }
 }
index 2ce1a86..3a957dc 100644 (file)
@@ -140,6 +140,22 @@ class behat_data_generators extends behat_base {
             'datagenerator' => 'grade_category',
             'required' => array('fullname', 'course'),
             'switchids' => array('course' => 'courseid', 'gradecategory' => 'parent')
+        ),
+        'grade items' => array(
+            'datagenerator' => 'grade_item',
+            'required' => array('course'),
+            'switchids' => array('scale' => 'scaleid', 'outcome' => 'outcomeid', 'course' => 'courseid',
+                                 'gradecategory' => 'categoryid')
+        ),
+        'grade outcomes' => array(
+            'datagenerator' => 'grade_outcome',
+            'required' => array('shortname', 'scale'),
+            'switchids' => array('course' => 'courseid', 'gradecategory' => 'categoryid', 'scale' => 'scaleid')
+        ),
+        'scales' => array(
+            'datagenerator' => 'scale',
+            'required' => array('name', 'scale'),
+            'switchids' => array('course' => 'courseid')
         )
     );
 
@@ -246,6 +262,21 @@ class behat_data_generators extends behat_base {
         return $data;
     }
 
+    /**
+     * Preprocesses the creation of a grade item. Converts gradetype text to a number.
+     * @param array $data
+     * @return array
+     */
+    protected function preprocess_grade_item($data) {
+        global $CFG;
+        require_once("$CFG->libdir/grade/constants.php");
+
+        if (isset($data['gradetype'])) {
+            $data['gradetype'] = constant("GRADE_TYPE_" . strtoupper($data['gradetype']));
+        }
+        return $data;
+    }
+
     /**
      * Adapter to modules generator
      * @throws Exception Custom exception for test writers
@@ -253,12 +284,22 @@ class behat_data_generators extends behat_base {
      * @return void
      */
     protected function process_activity($data) {
-        global $DB;
+        global $DB, $CFG;
 
         // The the_following_exists() method checks that the field exists.
         $activityname = $data['activity'];
         unset($data['activity']);
 
+        // Convert scale name into scale id (negative number indicates using scale).
+        if (isset($data['grade']) && strlen($data['grade']) && !is_number($data['grade'])) {
+            $data['grade'] = - $this->get_scale_id($data['grade']);
+            require_once("$CFG->libdir/grade/constants.php");
+
+            if (!isset($data['gradetype'])) {
+                $data['gradetype'] = GRADE_TYPE_SCALE;
+            }
+        }
+
         // We split $data in the activity $record and the course module $options.
         $cmoptions = array();
         $cmcolumns = $DB->get_columns('course_modules');
@@ -559,6 +600,36 @@ class behat_data_generators extends behat_base {
         return $id;
     }
 
+    /**
+     * Gets the outcome item id from its shortname.
+     * @throws Exception
+     * @param string $shortname
+     * @return int
+     */
+    protected function get_outcome_id($shortname) {
+        global $DB;
+
+        if (!$id = $DB->get_field('grade_outcomes', 'id', array('shortname' => $shortname))) {
+            throw new Exception('The specified outcome with shortname "' . $shortname . '" does not exist');
+        }
+        return $id;
+    }
+
+    /**
+     * Gets the course id from its name.
+     * @throws Exception
+     * @param string $name
+     * @return int
+     */
+    protected function get_scale_id($name) {
+        global $DB;
+
+        if (!$id = $DB->get_field('scale', 'id', array('name' => $name))) {
+            throw new Exception('The specified scale with name "' . $name . '" does not exist');
+        }
+        return $id;
+    }
+
     /**
      * Gets the internal context id from the context reference.
      *