first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+265
View File
@@ -0,0 +1,265 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Definitions of constants for gradebook
*
* @package core_grades
* @category grade
* @copyright 2007 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// Category aggregation types
/**
* GRADE_AGGREGATE_MEAN - Use the category mean for grade aggregation.
*/
define('GRADE_AGGREGATE_MEAN', 0);
/**
* GRADE_AGGREGATE_MEDIAN - Use the category median for grade aggregation.
*/
define('GRADE_AGGREGATE_MEDIAN', 2);
/**
* GRADE_AGGREGATE_MIN - Use the category minimum grade for grade aggregation.
*/
define('GRADE_AGGREGATE_MIN', 4);
/**
* GRADE_AGGREGATE_MAX - Use the category maximum grade for grade aggregation.
*/
define('GRADE_AGGREGATE_MAX', 6);
/**
* GRADE_AGGREGATE_MEDIAN - Use the category mode for grade aggregation.
*/
define('GRADE_AGGREGATE_MODE', 8);
/**
* GRADE_AGGREGATE_WEIGHTED_MEAN - Use a weighted mean of grades in the category for grade aggregation. Weights can be manually set.
*/
define('GRADE_AGGREGATE_WEIGHTED_MEAN', 10);
/**
* GRADE_AGGREGATE_WEIGHTED_MEAN2 - Use a simple weighted mean of grades in the category for grade aggregation.
*/
define('GRADE_AGGREGATE_WEIGHTED_MEAN2', 11);
/**
* GRADE_AGGREGATE_EXTRACREDIT_MEAN - Use the category mean for grade aggregation and include support for extra credit.
*/
define('GRADE_AGGREGATE_EXTRACREDIT_MEAN', 12);
/**
* GRADE_AGGREGATE_SUM - Use Natural in the category for grade aggregation.
*/
define('GRADE_AGGREGATE_SUM', 13);
// Grade types
/**
* GRADE_TYPE_NONE - Ungraded.
*/
define('GRADE_TYPE_NONE', 0);
/**
* GRADE_TYPE_NONE - The grade is a numeric value
*/
define('GRADE_TYPE_VALUE', 1);
/**
* GRADE_TYPE_NONE - The grade is a value from the set of values available in a grade scale.
*/
define('GRADE_TYPE_SCALE', 2);
/**
* GRADE_TYPE_NONE - Feedback only.
*/
define('GRADE_TYPE_TEXT', 3);
// grade_update() return status
/**
* GRADE_UPDATE_OK - Grade updated completed successfully.
*/
define('GRADE_UPDATE_OK', 0);
/**
* GRADE_UPDATE_FAILED - Grade updated failed.
*/
define('GRADE_UPDATE_FAILED', 1);
/**
* GRADE_UPDATE_MULTIPLE - Grade update failed because there are multiple grade items with the same itemnumber for this activity.
*/
define('GRADE_UPDATE_MULTIPLE', 2);
/**
* GRADE_UPDATE_DELETED - Grade item cannot be updated as it is locked
*/
define('GRADE_UPDATE_ITEM_LOCKED', 4);
// Grade tables history tracking actions
/**
* GRADE_HISTORY_INSERT - A grade item was inserted
*/
define('GRADE_HISTORY_INSERT', 1);
/**
* GRADE_HISTORY_UPDATE - A grade item was updated
*/
define('GRADE_HISTORY_UPDATE', 2);
/**
* GRADE_HISTORY_INSERT - A grade item was deleted
*/
define('GRADE_HISTORY_DELETE', 3);
// Display style constants
/**
* GRADE_DISPLAY_TYPE_DEFAULT - Grade display type can be set at 3 levels: grade_item, course setting and site. Use the display type from the higher level.
*/
define('GRADE_DISPLAY_TYPE_DEFAULT', 0);
/**
* GRADE_DISPLAY_TYPE_REAL - Display the grade as a decimal number.
*/
define('GRADE_DISPLAY_TYPE_REAL', 1);
/**
* GRADE_DISPLAY_TYPE_PERCENTAGE - Display the grade as a percentage.
*/
define('GRADE_DISPLAY_TYPE_PERCENTAGE', 2);
/**
* GRADE_DISPLAY_TYPE_LETTER - Display the grade as a letter grade. For example, A, B, C, D or F.
*/
define('GRADE_DISPLAY_TYPE_LETTER', 3);
/**
* GRADE_DISPLAY_TYPE_REAL_PERCENTAGE - Display the grade as a decimal number and a percentage.
*/
define('GRADE_DISPLAY_TYPE_REAL_PERCENTAGE', 12);
/**
* GRADE_DISPLAY_TYPE_REAL_LETTER - Display the grade as a decimal number and a letter grade.
*/
define('GRADE_DISPLAY_TYPE_REAL_LETTER', 13);
/**
* GRADE_DISPLAY_TYPE_LETTER_REAL - Display the grade as a letter grade and a decimal number.
*/
define('GRADE_DISPLAY_TYPE_LETTER_REAL', 31);
/**
* GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE - Display the grade as a letter grade and a percentage.
*/
define('GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE', 32);
/**
* GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER - Display the grade as a percentage and a letter grade.
*/
define('GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER', 23);
/**
* GRADE_DISPLAY_TYPE_PERCENTAGE_REAL - Display the grade as a percentage and a decimal number.
*/
define('GRADE_DISPLAY_TYPE_PERCENTAGE_REAL', 21);
/**
* GRADE_REPORT_AGGREGATION_POSITION_FIRST - Display the course totals before the individual activity grades
*/
define('GRADE_REPORT_AGGREGATION_POSITION_FIRST', 0);
/**
* GRADE_REPORT_AGGREGATION_POSITION_LAST - Display the course totals after the individual activity grades
*/
define('GRADE_REPORT_AGGREGATION_POSITION_LAST', 1);
// What to do if category or course total contains a hidden item
/**
* GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN - If the category or course total contains a hidden item hide the total from students.
*/
define('GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN', 0);
/**
* GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN - If the category or course total contains a hidden item show the total to students minus grades from the hidden items.
*/
define('GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN', 1);
/**
* GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN - If the category or course total contains a hidden item show students the real total including marks from hidden items.
*/
define('GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN', 2);
/**
* GRADE_REPORT_PREFERENCE_DEFAULT - Use the setting from site preferences.
*/
define('GRADE_REPORT_PREFERENCE_DEFAULT', 'default');
/**
* GRADE_REPORT_PREFERENCE_INHERIT - Inherit the setting value from the parent.
*/
define('GRADE_REPORT_PREFERENCE_INHERIT', 'inherit'); // means inherit from parent
/**
* GRADE_REPORT_PREFERENCE_UNUSED - Unused constant.
*/
define('GRADE_REPORT_PREFERENCE_UNUSED', -1);
/**
* GRADE_REPORT_MEAN_ALL - Include all grade items including those where the student hasn't received a grade when calculating the mean.
*/
define('GRADE_REPORT_MEAN_ALL', 0);
/**
* GRADE_REPORT_MEAN_GRADED - Only include grade items where the student has a grade when calculating the mean.
*/
define('GRADE_REPORT_MEAN_GRADED', 1);
/**
* GRADE_MIN_MAX_FROM_GRADE_ITEM - Get the grade min/max from the grade item.
*/
define('GRADE_MIN_MAX_FROM_GRADE_ITEM', 1);
/**
* GRADE_MIN_MAX_FROM_GRADE_GRADE - Get the grade min/max from the grade grade.
*/
define('GRADE_MIN_MAX_FROM_GRADE_GRADE', 2);
/**
* The component to store grade files.
*/
define('GRADE_FILE_COMPONENT', 'grade');
/**
* The file area to store the associated grade_grades feedback files.
*/
define('GRADE_FEEDBACK_FILEAREA', 'feedback');
/**
* The file area to store the associated grade_grades_history feedback files.
*/
define('GRADE_HISTORY_FEEDBACK_FILEAREA', 'historyfeedback');
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+497
View File
@@ -0,0 +1,497 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Definition of a grade object class for grade item, grade category etc to inherit from
*
* @package core_grades
* @category grade
* @copyright 2006 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* An abstract object that holds methods and attributes common to all grade_* objects defined here.
*
* @package core_grades
* @category grade
* @copyright 2006 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class grade_object {
/**
* The database table this grade object is stored in
* @var string $table
*/
public $table;
/**
* Array of required table fields, must start with 'id'.
* @var array $required_fields
*/
public $required_fields = array('id', 'timecreated', 'timemodified', 'hidden');
/**
* Array of optional fields with default values - usually long text information that is not always needed.
* If you want to create an instance without optional fields use: new grade_object($only_required_fields, false);
* @var array $optional_fields
*/
public $optional_fields = array();
/**
* The PK.
* @var int $id
*/
public $id;
/**
* The first time this grade_object was created.
* @var int $timecreated
*/
public $timecreated;
/**
* The last time this grade_object was modified.
* @var int $timemodified
*/
public $timemodified;
/**
* 0 if visible, 1 always hidden or date not visible until
* @var int $hidden
*/
var $hidden = 0;
/**
* Constructor. Optionally (and by default) attempts to fetch corresponding row from the database
*
* @param array $params An array with required parameters for this grade object.
* @param bool $fetch Whether to fetch corresponding row from the database or not,
* optional fields might not be defined if false used
*/
public function __construct($params=NULL, $fetch=true) {
if (!empty($params) and (is_array($params) or is_object($params))) {
if ($fetch) {
if ($data = $this->fetch($params)) {
grade_object::set_properties($this, $data);
} else {
grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
grade_object::set_properties($this, $params);
}
} else {
grade_object::set_properties($this, $params);
}
} else {
grade_object::set_properties($this, $this->optional_fields);//apply defaults for optional fields
}
}
/**
* Makes sure all the optional fields are loaded.
*
* If id present, meaning the instance exists in the database, then data will be fetched from the database.
* Defaults are used for new instances.
*/
public function load_optional_fields() {
global $DB;
foreach ($this->optional_fields as $field=>$default) {
if (property_exists($this, $field)) {
continue;
}
if (empty($this->id)) {
$this->$field = $default;
} else {
$this->$field = $DB->get_field($this->table, $field, array('id', $this->id));
}
}
}
/**
* Finds and returns a grade_object instance based on params.
*
* @static
* @abstract
* @param array $params associative arrays varname=>value
* @return object grade_object instance or false if none found.
*/
public static function fetch($params) {
throw new coding_exception('fetch() method needs to be overridden in each subclass of grade_object');
}
/**
* Finds and returns all grade_object instances based on $params.
*
* @static
* @abstract
* @throws coding_exception Throws a coding exception if fetch_all() has not been overriden by the grade object subclass
* @param array $params Associative arrays varname=>value
* @return array|bool Array of grade_object instances or false if none found.
*/
public static function fetch_all($params) {
throw new coding_exception('fetch_all() method needs to be overridden in each subclass of grade_object');
}
/**
* Factory method which uses the parameters to retrieve matching instances from the database
*
* @param string $table The table to retrieve from
* @param string $classname The name of the class to instantiate
* @param array $params An array of conditions like $fieldname => $fieldvalue
* @return mixed An object instance or false if not found
*/
protected static function fetch_helper($table, $classname, $params) {
if ($instances = grade_object::fetch_all_helper($table, $classname, $params)) {
if (count($instances) > 1) {
// we should not tolerate any errors here - problems might appear later
throw new \moodle_exception('morethanonerecordinfetch', 'debug');
}
return reset($instances);
} else {
return false;
}
}
/**
* Factory method which uses the parameters to retrieve all matching instances from the database
*
* @param string $table The table to retrieve from
* @param string $classname The name of the class to instantiate
* @param array $params An array of conditions like $fieldname => $fieldvalue
* @return array|bool Array of object instances or false if not found
*/
public static function fetch_all_helper($table, $classname, $params) {
global $DB; // Need to introspect DB here.
$instance = new $classname();
$classvars = (array)$instance;
$params = (array)$params;
$wheresql = array();
$newparams = array();
$columns = $DB->get_columns($table); // Cached, no worries.
foreach ($params as $var=>$value) {
if (!in_array($var, $instance->required_fields) and !array_key_exists($var, $instance->optional_fields)) {
continue;
}
if (!array_key_exists($var, $columns)) {
continue;
}
if (is_null($value)) {
$wheresql[] = " $var IS NULL ";
} else {
if ($columns[$var]->meta_type === 'X') {
// We have a text/clob column, use the cross-db method for its comparison.
$wheresql[] = ' ' . $DB->sql_compare_text($var) . ' = ' . $DB->sql_compare_text('?') . ' ';
} else {
// Other columns (varchar, integers...).
$wheresql[] = " $var = ? ";
}
$newparams[] = $value;
}
}
if (empty($wheresql)) {
$wheresql = '';
} else {
$wheresql = implode("AND", $wheresql);
}
global $DB;
$rs = $DB->get_recordset_select($table, $wheresql, $newparams);
//returning false rather than empty array if nothing found
if (!$rs->valid()) {
$rs->close();
return false;
}
$result = array();
foreach($rs as $data) {
$instance = new $classname();
grade_object::set_properties($instance, $data);
$result[$instance->id] = $instance;
}
$rs->close();
return $result;
}
/**
* Updates this object in the Database, based on its object variables. ID must be set.
*
* @param string $source from where was the object updated (mod/forum, manual, etc.)
* @param bool $isbulkupdate If bulk grade update is happening.
* @return bool success
*/
public function update($source = null, $isbulkupdate = false) {
global $USER, $CFG, $DB;
if (empty($this->id)) {
debugging('Can not update grade object, no id!');
return false;
}
$data = $this->get_record_data();
$DB->update_record($this->table, $data);
$historyid = null;
if (empty($CFG->disablegradehistory)) {
unset($data->timecreated);
$data->action = GRADE_HISTORY_UPDATE;
$data->oldid = $this->id;
$data->source = $source;
$data->timemodified = time();
$data->loggeduser = $USER->id;
$historyid = $DB->insert_record($this->table.'_history', $data);
}
$this->notify_changed(false, $isbulkupdate);
$this->update_feedback_files($historyid);
return true;
}
/**
* Deletes this object from the database.
*
* @param string $source From where was the object deleted (mod/forum, manual, etc.)
* @return bool success
*/
public function delete($source=null) {
global $USER, $CFG, $DB;
if (empty($this->id)) {
debugging('Can not delete grade object, no id!');
return false;
}
$data = $this->get_record_data();
if ($DB->delete_records($this->table, array('id'=>$this->id))) {
if (empty($CFG->disablegradehistory)) {
unset($data->id);
unset($data->timecreated);
$data->action = GRADE_HISTORY_DELETE;
$data->oldid = $this->id;
$data->source = $source;
$data->timemodified = time();
$data->loggeduser = $USER->id;
$DB->insert_record($this->table.'_history', $data);
}
$this->notify_changed(true);
$this->delete_feedback_files();
return true;
} else {
return false;
}
}
/**
* Returns object with fields and values that are defined in database
*
* @return stdClass
*/
public function get_record_data() {
$data = new stdClass();
foreach ($this as $var=>$value) {
if (in_array($var, $this->required_fields) or array_key_exists($var, $this->optional_fields)) {
if (is_object($value) or is_array($value)) {
debugging("Incorrect property '$var' found when inserting grade object");
} else {
$data->$var = $value;
}
}
}
return $data;
}
/**
* Records this object in the Database, sets its id to the returned value, and returns that value.
* If successful this function also fetches the new object data from database and stores it
* in object properties.
*
* @param string $source From where was the object inserted (mod/forum, manual, etc.)
* @param string $isbulkupdate If bulk grade update is happening.
* @return int The new grade object ID if successful, false otherwise
*/
public function insert($source = null, $isbulkupdate = false) {
global $USER, $CFG, $DB;
if (!empty($this->id)) {
debugging("Grade object already exists!");
return false;
}
$data = $this->get_record_data();
$this->id = $DB->insert_record($this->table, $data);
// set all object properties from real db data
$this->update_from_db();
$data = $this->get_record_data();
$historyid = null;
if (empty($CFG->disablegradehistory)) {
unset($data->timecreated);
$data->action = GRADE_HISTORY_INSERT;
$data->oldid = $this->id;
$data->source = $source;
$data->timemodified = time();
$data->loggeduser = $USER->id;
$historyid = $DB->insert_record($this->table.'_history', $data);
}
$this->notify_changed(false, $isbulkupdate);
$this->add_feedback_files($historyid);
return $this->id;
}
/**
* Using this object's id field, fetches the matching record in the DB, and looks at
* each variable in turn. If the DB has different data, the db's data is used to update
* the object. This is different from the update() function, which acts on the DB record
* based on the object.
*
* @return bool True if successful
*/
public function update_from_db() {
if (empty($this->id)) {
debugging("The object could not be used in its state to retrieve a matching record from the DB, because its id field is not set.");
return false;
}
global $DB;
if (!$params = $DB->get_record($this->table, array('id' => $this->id))) {
debugging("Object with this id:{$this->id} does not exist in table:{$this->table}, can not update from db!");
return false;
}
grade_object::set_properties($this, $params);
return true;
}
/**
* Given an associated array or object, cycles through each key/variable
* and assigns the value to the corresponding variable in this object.
*
* @param grade_object $instance The object to set the properties on
* @param array $params An array of properties to set like $propertyname => $propertyvalue
* @return array|stdClass Either an associative array or an object containing property name, property value pairs
*/
public static function set_properties(&$instance, $params) {
$params = (array) $params;
foreach ($params as $var => $value) {
if (in_array($var, $instance->required_fields) or array_key_exists($var, $instance->optional_fields)) {
$instance->$var = $value;
}
}
}
/**
* Called immediately after the object data has been inserted, updated, or
* deleted in the database. Default does nothing, can be overridden to
* hook in special behaviour.
*
* @param bool $deleted
*/
protected function notify_changed($deleted) {
}
/**
* Handles adding feedback files in the gradebook.
*
* @param int|null $historyid
*/
protected function add_feedback_files(int $historyid = null) {
}
/**
* Handles updating feedback files in the gradebook.
*
* @param int|null $historyid
*/
protected function update_feedback_files(int $historyid = null) {
}
/**
* Handles deleting feedback files in the gradebook.
*/
protected function delete_feedback_files() {
}
/**
* Returns the current hidden state of this grade_item
*
* This depends on the grade object hidden setting and the current time if hidden is set to a "hidden until" timestamp
*
* @return bool Current hidden state
*/
function is_hidden() {
return ($this->hidden == 1 or ($this->hidden != 0 and $this->hidden > time()));
}
/**
* Check grade object hidden status
*
* @return bool True if a "hidden until" timestamp is set, false if grade object is set to always visible or always hidden.
*/
function is_hiddenuntil() {
return $this->hidden > 1;
}
/**
* Check a grade item hidden status.
*
* @return int 0 means visible, 1 hidden always, a timestamp means "hidden until"
*/
function get_hidden() {
return $this->hidden;
}
/**
* Set a grade object hidden status
*
* @param int $hidden 0 means visiable, 1 means hidden always, a timestamp means "hidden until"
* @param bool $cascade Ignored
*/
function set_hidden($hidden, $cascade=false) {
$this->hidden = $hidden;
$this->update();
}
/**
* Returns whether the grade object can control the visibility of the grades.
*
* @return bool
*/
public function can_control_visibility() {
return true;
}
}
+429
View File
@@ -0,0 +1,429 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Definition of grade outcome class
*
* @package core_grades
* @category grade
* @copyright 2006 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once('grade_object.php');
/**
* Class representing a grade outcome.
*
* It is responsible for handling its DB representation, modifying and returning its metadata.
*
* @package core_grades
* @category grade
* @copyright 2006 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_outcome extends grade_object {
/**
* DB Table (used by grade_object).
* @var string $table
*/
public $table = 'grade_outcomes';
/**
* Array of required table fields, must start with 'id'.
* @var array $required_fields
*/
public $required_fields = array('id', 'courseid', 'shortname', 'fullname', 'scaleid','description',
'descriptionformat', 'timecreated', 'timemodified', 'usermodified');
/**
* The course this outcome belongs to.
* @var int $courseid
*/
public $courseid;
/**
* The shortname of the outcome.
* @var string $shortname
*/
public $shortname;
/**
* The fullname of the outcome.
* @var string $fullname
*/
public $fullname;
/**
* A full grade_scale object referenced by $this->scaleid.
* @var object $scale
*/
public $scale;
/**
* The id of the scale referenced by this outcome.
* @var int $scaleid
*/
public $scaleid;
/**
* The description of this outcome - FORMAT_MOODLE.
* @var string $description
*/
public $description;
/**
* The userid of the person who last modified this outcome.
*
* @var int $usermodified
*/
public $usermodified;
/** @var int Identifier of the text format to be used. */
public $descriptionformat = FORMAT_MOODLE;
/**
* Deletes this outcome from the database.
*
* @param string $source from where was the object deleted (mod/forum, manual, etc.)
* @return bool success
*/
public function delete($source=null) {
global $DB;
if (!empty($this->courseid)) {
$DB->delete_records('grade_outcomes_courses', array('outcomeid' => $this->id, 'courseid' => $this->courseid));
}
if (parent::delete($source)) {
$context = context_system::instance();
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'grade', 'outcome', $this->id);
foreach ($files as $file) {
$file->delete();
}
return true;
}
return false;
}
/**
* Records this object in the Database, sets its id to the returned value, and returns that value.
* If successful this function also fetches the new object data from database and stores it
* in object properties.
*
* @param string $source from where was the object inserted (mod/forum, manual, etc.)
* @param bool $isbulkupdate If bulk grade update is happening.
* @return int PK ID if successful, false otherwise
*/
public function insert($source = null, $isbulkupdate = false) {
global $DB;
$this->timecreated = $this->timemodified = time();
if ($result = parent::insert($source)) {
if (!empty($this->courseid)) {
$goc = new stdClass();
$goc->courseid = $this->courseid;
$goc->outcomeid = $this->id;
$DB->insert_record('grade_outcomes_courses', $goc);
}
}
return $result;
}
/**
* In addition to update() it also updates grade_outcomes_courses if needed
*
* @param string $source from where was the object inserted
* @param bool $isbulkupdate If bulk grade update is happening.
* @return bool success
*/
public function update($source = null, $isbulkupdate = false) {
$this->timemodified = time();
if ($result = parent::update($source)) {
if (!empty($this->courseid)) {
$this->use_in($this->courseid);
}
}
return $result;
}
/**
* Mark outcome as used in a course
*
* @param int $courseid
* @return False if invalid courseid requested
*/
public function use_in($courseid) {
global $DB;
if (!empty($this->courseid) and $courseid != $this->courseid) {
return false;
}
if (!$DB->record_exists('grade_outcomes_courses', array('courseid' => $courseid, 'outcomeid' => $this->id))) {
$goc = new stdClass();
$goc->courseid = $courseid;
$goc->outcomeid = $this->id;
$DB->insert_record('grade_outcomes_courses', $goc);
}
return true;
}
/**
* Finds and returns a grade_outcome instance based on params.
*
* @static
* @param array $params associative arrays varname=>value
* @return object grade_outcome instance or false if none found.
*/
public static function fetch($params) {
return grade_object::fetch_helper('grade_outcomes', 'grade_outcome', $params);
}
/**
* Finds and returns all grade_outcome instances based on params.
*
* @static
* @param array $params associative arrays varname=>value
* @return array array of grade_outcome insatnces or false if none found.
*/
public static function fetch_all($params) {
return grade_object::fetch_all_helper('grade_outcomes', 'grade_outcome', $params);
}
/**
* Instantiates a grade_scale object whose data is retrieved from the database
*
* @return grade_scale
*/
public function load_scale() {
if (empty($this->scale->id) or $this->scale->id != $this->scaleid) {
$this->scale = grade_scale::fetch(array('id'=>$this->scaleid));
$this->scale->load_items();
}
return $this->scale;
}
/**
* Static function returning all global outcomes
*
* @static
* @return array
*/
public static function fetch_all_global() {
if (!$outcomes = grade_outcome::fetch_all(array('courseid'=>null))) {
$outcomes = array();
}
return $outcomes;
}
/**
* Static function returning all local course outcomes
*
* @static
* @param int $courseid
* @return array
*/
public static function fetch_all_local($courseid) {
if (!$outcomes =grade_outcome::fetch_all(array('courseid'=>$courseid))) {
$outcomes = array();
}
return $outcomes;
}
/**
* Static method that returns all outcomes available in course
*
* @static
* @param int $courseid
* @return array
*/
public static function fetch_all_available($courseid) {
global $CFG, $DB;
$result = array();
$params = array($courseid);
$sql = "SELECT go.*
FROM {grade_outcomes} go, {grade_outcomes_courses} goc
WHERE go.id = goc.outcomeid AND goc.courseid = ?
ORDER BY go.id ASC";
if ($datas = $DB->get_records_sql($sql, $params)) {
foreach($datas as $data) {
$instance = new grade_outcome();
grade_object::set_properties($instance, $data);
$result[$instance->id] = $instance;
}
}
return $result;
}
/**
* Returns the most descriptive field for this object. This is a standard method used
* when we do not know the exact type of an object.
*
* @return string name
*/
public function get_name() {
// Grade outcomes can be created at site or course context, so set the filter context appropriately.
$context = empty($this->courseid) ? context_system::instance() : context_course::instance($this->courseid);
return format_string($this->fullname, false, ["context" => $context]);
}
/**
* Returns unique outcome short name.
*
* @return string name
*/
public function get_shortname() {
return $this->shortname;
}
/**
* Returns the formatted grade description with URLs converted
*
* @return string
*/
public function get_description() {
global $CFG;
require_once($CFG->libdir . '/filelib.php');
$options = new stdClass;
$options->noclean = true;
$systemcontext = context_system::instance();
$description = file_rewrite_pluginfile_urls($this->description, 'pluginfile.php', $systemcontext->id, 'grade', 'outcome', $this->id);
return format_text($description, $this->descriptionformat, $options);
}
/**
* Checks if outcome can be deleted.
*
* @return bool
*/
public function can_delete() {
if ($this->get_item_uses_count()) {
return false;
}
if (empty($this->courseid)) {
if ($this->get_course_uses_count()) {
return false;
}
}
return true;
}
/**
* Returns the number of places where outcome is used.
*
* @return int
*/
public function get_course_uses_count() {
global $DB;
if (!empty($this->courseid)) {
return 1;
}
return $DB->count_records('grade_outcomes_courses', array('outcomeid' => $this->id));
}
/**
* Returns the number of grade items that use this grade outcome
*
* @return int
*/
public function get_item_uses_count() {
global $DB;
return $DB->count_records('grade_items', array('outcomeid' => $this->id));
}
/**
* Computes then returns extra information about this outcome and other objects that are linked to it.
* The average of all grades that use this outcome, for all courses (or 1 course if courseid is given) can
* be requested, and is returned as a float if requested alone. If the list of items that use this outcome
* is also requested, then a single array is returned, which contains the grade_items AND the average grade
* if such is still requested (array('items' => array(...), 'avg' => 2.30)). This combining of two
* methods into one is to save on DB queries, since both queries are similar and can be performed together.
*
* @param int $courseid An optional courseid to narrow down the average to 1 course only
* @param bool $average Whether or not to return the average grade for this outcome
* @param bool $items Whether or not to return the list of items using this outcome
* @return float
*/
public function get_grade_info($courseid=null, $average=true, $items=false) {
global $CFG, $DB;
if (!isset($this->id)) {
debugging("You must setup the outcome's id before calling its get_grade_info() method!");
return false; // id must be defined for this to work
}
if ($average === false && $items === false) {
debugging('Either the 1st or 2nd param of grade_outcome::get_grade_info() must be true, or both, but not both false!');
return false;
}
$params = array($this->id);
$wheresql = '';
if (!is_null($courseid)) {
$wheresql = " AND {grade_items}.courseid = ? ";
$params[] = $courseid;
}
$selectadd = '';
if ($items !== false) {
$selectadd = ", {grade_items}.* ";
}
$sql = "SELECT finalgrade $selectadd
FROM {grade_grades}, {grade_items}, {grade_outcomes}
WHERE {grade_outcomes}.id = {grade_items}.outcomeid
AND {grade_items}.id = {grade_grades}.itemid
AND {grade_outcomes}.id = ?
$wheresql";
$grades = $DB->get_records_sql($sql, $params);
$retval = array();
if ($average !== false && count($grades) > 0) {
$count = 0;
$total = 0;
foreach ($grades as $k => $grade) {
// Skip null finalgrades
if (!is_null($grade->finalgrade)) {
$total += $grade->finalgrade;
$count++;
}
unset($grades[$k]->finalgrade);
}
$retval['avg'] = $total / $count;
}
if ($items !== false) {
foreach ($grades as $grade) {
$retval['items'][$grade->id] = new grade_item($grade);
}
}
return $retval;
}
}
+404
View File
@@ -0,0 +1,404 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Definition of grade scale class
*
* @package core_grades
* @category grade
* @copyright 2006 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once('grade_object.php');
/**
* Class representing a grade scale.
*
* It is responsible for handling its DB representation, modifying and returning its metadata.
*
* @package core_grades
* @category grade
* @copyright 2006 Nicolas Connault
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_scale extends grade_object {
/**
* DB Table (used by grade_object).
* @var string $table
*/
public $table = 'scale';
/**
* Array of required table fields, must start with 'id'.
* @var array $required_fields
*/
public $required_fields = array('id', 'courseid', 'userid', 'name', 'scale', 'description', 'descriptionformat', 'timemodified');
/**
* The course this scale belongs to.
* @var int $courseid
*/
public $courseid;
/**
* The ID of the user who created the scale
* @var int $userid
*/
public $userid;
/**
* The name of the scale.
* @var string $name
*/
public $name;
/**
* The items in this scale.
* @var array $scale_items
*/
public $scale_items = array();
/**
* A string representation of the scale items (a comma-separated list).
* @var string $scale
*/
public $scale;
/**
* A description for this scale.
* @var string $description
*/
public $description;
/**
* Standard event.
* @var bool $standard
*/
public $standard;
/**
* Identifier of the text format to be used.
* @var int $descriptionformat
*/
public int $descriptionformat;
/**
* Finds and returns a grade_scale instance based on params.
*
* @static
* @param array $params associative arrays varname=>value
* @return object grade_scale instance or false if none found.
*/
public static function fetch($params) {
return grade_object::fetch_helper('scale', 'grade_scale', $params);
}
/**
* Finds and returns all grade_scale instances based on params.
*
* @static
* @param array $params associative arrays varname=>value
* @return array array of grade_scale instances or false if none found.
*/
public static function fetch_all($params) {
return grade_object::fetch_all_helper('scale', 'grade_scale', $params);
}
/**
* Records this object in the Database, sets its id to the returned value, and returns that value.
* If successful this function also fetches the new object data from database and stores it
* in object properties.
*
* @param string $source from where was the object inserted (mod/forum, manual, etc.)
* @param bool $isbulkupdate If bulk grade update is happening.
* @return int PK ID if successful, false otherwise
*/
public function insert($source = null, $isbulkupdate = false) {
$this->timecreated = time();
$this->timemodified = time();
$result = parent::insert($source);
if ($result) {
// Trigger the scale created event.
if (!empty($this->standard)) {
$eventcontext = context_system::instance();
} else {
if (!empty($this->courseid)) {
$eventcontext = context_course::instance($this->courseid);
} else {
$eventcontext = context_system::instance();
}
}
$event = \core\event\scale_created::create(array(
'objectid' => $result,
'context' => $eventcontext
));
$event->trigger();
}
return $result;
}
/**
* In addition to update() it also updates grade_outcomes_courses if needed
*
* @param string $source from where was the object inserted
* @param bool $isbulkupdate If bulk grade update is happening.
* @return bool success
*/
public function update($source = null, $isbulkupdate = false) {
$this->timemodified = time();
$result = parent::update($source);
if ($result) {
// Trigger the scale updated event.
if (!empty($this->standard)) {
$eventcontext = context_system::instance();
} else {
if (!empty($this->courseid)) {
$eventcontext = context_course::instance($this->courseid);
} else {
$eventcontext = context_system::instance();
}
}
$event = \core\event\scale_updated::create(array(
'objectid' => $this->id,
'context' => $eventcontext
));
$event->trigger();
}
return $result;
}
/**
* Deletes this scale from the database.
*
* @param string $source from where was the object deleted (mod/forum, manual, etc.)
* @return bool success
*/
public function delete($source=null) {
global $DB;
// Trigger the scale deleted event.
if (!empty($this->standard)) {
$eventcontext = context_system::instance();
} else {
if (!empty($this->courseid)) {
$eventcontext = context_course::instance($this->courseid);
} else {
$eventcontext = context_system::instance();
}
}
$event = \core\event\scale_deleted::create(array(
'objectid' => $this->id,
'context' => $eventcontext
));
$event->trigger();
if (parent::delete($source)) {
$context = context_system::instance();
$fs = get_file_storage();
$files = $fs->get_area_files($context->id, 'grade', 'scale', $this->id);
foreach ($files as $file) {
$file->delete();
}
return true;
}
return false;
}
/**
* Returns the most descriptive field for this object. This is a standard method used
* when we do not know the exact type of an object.
*
* @return string name
*/
public function get_name() {
// Grade scales can be created at site or course context, so set the filter context appropriately.
$context = empty($this->courseid) ? context_system::instance() : context_course::instance($this->courseid);
return format_string($this->name, false, ['context' => $context]);
}
/**
* Loads the scale's items into the $scale_items array.
* There are three ways to achieve this:
* 1. No argument given: The $scale string is already loaded and exploded to an array of items.
* 2. A string is given: A comma-separated list of items is exploded into an array of items.
* 3. An array of items is given and saved directly as the array of items for this scale.
*
* @param mixed $items Could be null, a string or an array. The method behaves differently for each case.
* @return array The resulting array of scale items or null if the method failed to produce one.
*/
public function load_items($items=NULL) {
if (empty($items)) {
$this->scale_items = explode(',', $this->scale);
} elseif (is_array($items)) {
$this->scale_items = $items;
} else {
$this->scale_items = explode(',', $items);
}
// Trim whitespace around each value
foreach ($this->scale_items as $key => $val) {
$this->scale_items[$key] = trim($val);
}
return $this->scale_items;
}
/**
* Compacts (implodes) the array of items in $scale_items into a comma-separated string, $scale.
* There are three ways to achieve this:
* 1. No argument given: The $scale_items array is already loaded and imploded to a string of items.
* 2. An array is given and is imploded into a string of items.
* 3. A string of items is given and saved directly as the $scale variable.
* NOTE: This method is the exact reverse of load_items, and their input/output should be interchangeable. However,
* because load_items() trims the whitespace around the items, when the string is reconstructed these whitespaces will
* be missing. This is not an issue, but should be kept in mind when comparing the two strings.
*
* @param mixed $items Could be null, a string or an array. The method behaves differently for each case.
* @return array The resulting string of scale items or null if the method failed to produce one.
*/
public function compact_items($items=NULL) {
if (empty($items)) {
$this->scale = implode(',', $this->scale_items);
} elseif (is_array($items)) {
$this->scale = implode(',', $items);
} else {
$this->scale = $items;
}
return $this->scale;
}
/**
* When called on a loaded scale object (with a valid id) and given a float grade between
* the grademin and grademax, this method returns the scale item that falls closest to the
* float given (which is usually an average of several grades on a scale). If the float falls
* below 1 but above 0, it will be rounded up to 1.
*
* @param float $grade
* @return string
*/
public function get_nearest_item($grade) {
global $DB;
// Obtain nearest scale item from average
$scales_array = $DB->get_records('scale', array('id' => $this->id));
$scale = $scales_array[$this->id];
$scales = explode(",", $scale->scale);
// this could be a 0 when summed and rounded, e.g, 1, no grade, no grade, no grade
if ($grade < 1) {
$grade = 1;
}
return $scales[$grade-1];
}
/**
* Static function returning all global scales
*
* @return object
*/
public static function fetch_all_global() {
return grade_scale::fetch_all(array('courseid'=>0));
}
/**
* Static function returning all local course scales
*
* @param int $courseid The course ID
* @return array Returns an array of grade_scale instances
*/
public static function fetch_all_local($courseid) {
return grade_scale::fetch_all(array('courseid'=>$courseid));
}
/**
* Checks if this is the last scale on the site.
*
* @return bool
*/
public function is_last_global_scale() {
return ($this->courseid == 0) && (count(self::fetch_all_global()) == 1);
}
/**
* Checks if scale can be deleted.
*
* @return bool
*/
public function can_delete() {
return !$this->is_used() && !$this->is_last_global_scale();
}
/**
* Returns if scale used anywhere - activities, grade items, outcomes, etc.
*
* @return bool
*/
public function is_used() {
global $DB;
global $CFG;
// count grade items excluding the
$params = array($this->id);
$sql = "SELECT COUNT(id) FROM {grade_items} WHERE scaleid = ? AND outcomeid IS NULL";
if ($DB->count_records_sql($sql, $params)) {
return true;
}
// count outcomes
$sql = "SELECT COUNT(id) FROM {grade_outcomes} WHERE scaleid = ?";
if ($DB->count_records_sql($sql, $params)) {
return true;
}
// Ask the competency subsystem.
if (\core_competency\api::is_scale_used_anywhere($this->id)) {
return true;
}
// Ask all plugins if the scale is used anywhere.
$pluginsfunction = get_plugins_with_function('scale_used_anywhere');
foreach ($pluginsfunction as $plugintype => $plugins) {
foreach ($plugins as $pluginfunction) {
if ($pluginfunction($this->id)) {
return true;
}
}
}
return false;
}
/**
* Returns the formatted grade description with URLs converted
*
* @return string
*/
public function get_description() {
global $CFG;
require_once($CFG->libdir . '/filelib.php');
$systemcontext = context_system::instance();
$options = new stdClass;
$options->noclean = true;
$description = file_rewrite_pluginfile_urls($this->description, 'pluginfile.php', $systemcontext->id, 'grade', 'scale', $this->id);
return format_text($description, $this->descriptionformat, $options);
}
}
+989
View File
@@ -0,0 +1,989 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* @package core_grades
* @category phpunit
* @copyright nicolas@moodle.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/gradelib.php');
/**
* Shared code for all grade related tests.
*
* Here is a brief explanation of the test data set up in these unit tests.
* category1 => array(category2 => array(grade_item1, grade_item2), category3 => array(grade_item3))
* 3 users for 3 grade_items
*/
abstract class grade_base_testcase extends advanced_testcase {
protected $course;
protected $activities = array();
protected $grade_items = array();
protected $grade_categories = array();
protected $grade_grades = array();
protected $grade_outcomes = array();
protected $scale = array();
protected $scalemax = array();
protected $courseid;
protected $userid;
/** @var array user object collection. */
protected $user = [];
/** @var array module object collection. */
protected $course_module = [];
protected function setUp(): void {
global $CFG;
parent::setup();
$this->resetAfterTest(true);
$CFG->grade_droplow = -1;
$CFG->grade_keephigh = -1;
$CFG->grade_aggregation = -1;
$CFG->grade_aggregateonlygraded = -1;
$CFG->grade_aggregateoutcomes = -1;
$this->course = $this->getDataGenerator()->create_course();
$this->courseid = $this->course->id;
$this->user[0] = $this->getDataGenerator()->create_user();
$this->user[1] = $this->getDataGenerator()->create_user();
$this->user[2] = $this->getDataGenerator()->create_user();
$this->user[3] = $this->getDataGenerator()->create_user();
$this->userid = $this->user[0]->id;
$this->load_modules();
$this->load_scales();
$this->load_grade_categories();
$this->load_grade_items();
$this->load_grade_grades();
$this->load_grade_outcomes();
}
private function load_modules() {
$this->activities[0] = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id));
$this->course_module[0] = get_coursemodule_from_instance('assign', $this->activities[0]->id);
$this->activities[1] = $this->getDataGenerator()->create_module('assign', array('course'=>$this->course->id));
$this->course_module[1] = get_coursemodule_from_instance('assign', $this->activities[1]->id);
$this->activities[2] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id));
$this->course_module[2] = get_coursemodule_from_instance('forum', $this->activities[2]->id);
$this->activities[3] = $this->getDataGenerator()->create_module('page', array('course'=>$this->course->id));
$this->course_module[3] = get_coursemodule_from_instance('page', $this->activities[3]->id);
$this->activities[4] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id));
$this->course_module[4] = get_coursemodule_from_instance('forum', $this->activities[4]->id);
$this->activities[5] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id));
$this->course_module[5] = get_coursemodule_from_instance('forum', $this->activities[5]->id);
$this->activities[6] = $this->getDataGenerator()->create_module('forum', array('course'=>$this->course->id));
$this->course_module[6] = get_coursemodule_from_instance('forum', $this->activities[6]->id);
$this->activities[7] = $this->getDataGenerator()->create_module('quiz', array('course'=>$this->course->id));
$this->course_module[7] = get_coursemodule_from_instance('quiz', $this->activities[7]->id);
}
private function load_scales() {
$scale = new stdClass();
$scale->name = 'unittestscale1';
$scale->courseid = $this->course->id;
$scale->userid = $this->user[0]->id;
$scale->scale = 'Way off topic, Not very helpful, Fairly neutral, Fairly helpful, Supportive, Some good information, Perfect answer!';
$scale->description = 'This scale defines some of qualities that make posts helpful within the Moodle help forums.\n Your feedback will help others see how their posts are being received.';
$this->scale[0] = $this->getDataGenerator()->create_scale($scale);
$this->scalemax[0] = substr_count($scale->scale, ',');
$scale = new stdClass();
$scale->name = 'unittestscale2';
$scale->courseid = $this->course->id;
$scale->userid = $this->user[0]->id;
$scale->scale = 'Distinction, Very Good, Good, Pass, Fail';
$scale->description = 'This scale is used to mark standard assignments.';
$this->scale[1] = $this->getDataGenerator()->create_scale($scale);
$this->scalemax[1] = substr_count($scale->scale, ',');
$scale = new stdClass();
$scale->name = 'unittestscale3';
$scale->courseid = $this->course->id;
$scale->userid = $this->user[0]->id;
$scale->scale = 'Loner, Contentious, Disinterested, Participative, Follower, Leader';
$scale->description = 'Describes the level of teamwork of a student.';
$temp = explode(',', $scale->scale);
$scale->max = count($temp) -1;
$this->scale[2] = $this->getDataGenerator()->create_scale($scale);
$this->scalemax[2] = substr_count($scale->scale, ',');
$scale = new stdClass();
$scale->name = 'unittestscale4';
$scale->courseid = $this->course->id;
$scale->userid = $this->user[0]->id;
$scale->scale = 'Does not understand theory, Understands theory but fails practice, Manages through, Excels';
$scale->description = 'Level of expertise at a technical task, with a theoretical framework.';
$temp = explode(',', $scale->scale);
$scale->max = count($temp) -1;
$this->scale[3] = $this->getDataGenerator()->create_scale($scale);
$this->scalemax[3] = substr_count($scale->scale, ',');
$scale = new stdClass();
$scale->name = 'unittestscale5';
$scale->courseid = $this->course->id;
$scale->userid = $this->user[0]->id;
$scale->scale = 'Insufficient, Acceptable, Excellent.';
$scale->description = 'Description of skills.';
$this->scale[4] = $this->getDataGenerator()->create_scale($scale);
$this->scalemax[4] = substr_count($scale->scale, ',');
}
/**
* Load grade_category data into the database, and adds the corresponding objects to this class' variable.
* category structure:
course category
|
+--------+-------------+-----------------------+
| | |
unittestcategory1 level1category unittestcategory7
| |
+--------+-------------+ +------------+---------------+
| | | |
unittestcategory2 unittestcategory3 unittestcategory5 unittestcategory6
*/
private function load_grade_categories() {
global $DB;
$course_category = grade_category::fetch_course_category($this->course->id);
$grade_category = new stdClass();
$grade_category->fullname = 'unittestcategory1 &';
$grade_category->courseid = $this->course->id;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 0;
$grade_category->droplow = 0;
$grade_category->parent = $course_category->id;
$grade_category->timecreated = time();
$grade_category->timemodified = time();
$grade_category->depth = 2;
$grade_category->id = $DB->insert_record('grade_categories', $grade_category);
$grade_category->path = '/'.$course_category->id.'/'.$grade_category->id.'/';
$DB->update_record('grade_categories', $grade_category);
$this->grade_categories[0] = $grade_category;
$grade_category = new stdClass();
$grade_category->fullname = 'unittestcategory2';
$grade_category->courseid = $this->course->id;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 0;
$grade_category->droplow = 0;
$grade_category->parent = $this->grade_categories[0]->id;
$grade_category->timecreated = time();
$grade_category->timemodified = time();
$grade_category->depth = 3;
$grade_category->id = $DB->insert_record('grade_categories', $grade_category);
$grade_category->path = $this->grade_categories[0]->path.$grade_category->id.'/';
$DB->update_record('grade_categories', $grade_category);
$this->grade_categories[1] = $grade_category;
$grade_category = new stdClass();
$grade_category->fullname = 'unittestcategory3';
$grade_category->courseid = $this->course->id;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 0;
$grade_category->droplow = 0;
$grade_category->parent = $this->grade_categories[0]->id;
$grade_category->timecreated = time();
$grade_category->timemodified = time();
$grade_category->depth = 3;
$grade_category->id = $DB->insert_record('grade_categories', $grade_category);
$grade_category->path = $this->grade_categories[0]->path.$grade_category->id.'/';
$DB->update_record('grade_categories', $grade_category);
$this->grade_categories[2] = $grade_category;
// A category with no parent, but grade_items as children.
$grade_category = new stdClass();
$grade_category->fullname = 'level1category';
$grade_category->courseid = $this->course->id;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 0;
$grade_category->droplow = 0;
$grade_category->parent = $course_category->id;
$grade_category->timecreated = time();
$grade_category->timemodified = time();
$grade_category->depth = 2;
$grade_category->id = $DB->insert_record('grade_categories', $grade_category);
$grade_category->path = '/'.$course_category->id.'/'.$grade_category->id.'/';
$DB->update_record('grade_categories', $grade_category);
$this->grade_categories[3] = $grade_category;
$grade_category = new stdClass();
$grade_category->fullname = 'unittestcategory7';
$grade_category->courseid = $this->course->id;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 0;
$grade_category->droplow = 0;
$grade_category->parent = $course_category->id;
$grade_category->timecreated = time();
$grade_category->timemodified = time();
$grade_category->depth = 2;
$grade_category->id = $DB->insert_record('grade_categories', $grade_category);
$grade_category->path = '/'.$course_category->id.'/'.$grade_category->id.'/';
$DB->update_record('grade_categories', $grade_category);
$this->grade_categories[4] = $grade_category;
$grade_category = new stdClass();
$grade_category->fullname = 'unittestcategory5';
$grade_category->courseid = $this->course->id;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 0;
$grade_category->droplow = 0;
$grade_category->parent = $this->grade_categories[4]->id;
$grade_category->timecreated = time();
$grade_category->timemodified = time();
$grade_category->depth = 3;
$grade_category->id = $DB->insert_record('grade_categories', $grade_category);
$grade_category->path = $this->grade_categories[4]->path.$grade_category->id.'/';
$DB->update_record('grade_categories', $grade_category);
$this->grade_categories[5] = $grade_category;
$grade_category = new stdClass();
$grade_category->fullname = 'unittestcategory6';
$grade_category->courseid = $this->course->id;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 0;
$grade_category->droplow = 0;
$grade_category->parent = $this->grade_categories[4]->id;
$grade_category->timecreated = time();
$grade_category->timemodified = time();
$grade_category->depth = 3;
$grade_category->id = $DB->insert_record('grade_categories', $grade_category);
$grade_category->path = $this->grade_categories[4]->path.$grade_category->id.'/';
$DB->update_record('grade_categories', $grade_category);
$this->grade_categories[6] = $grade_category;
}
/**
* Load grade_item data into the database, and adds the corresponding objects to this class' variable.
*/
protected function load_grade_items() {
global $DB;
// Purge all items created by module generators.
$DB->delete_records('grade_items', array('itemtype'=>'mod'));
$course_category = grade_category::fetch_course_category($this->course->id);
// id = 0
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[1]->id;
$grade_item->itemname = 'unittestgradeitem1 &';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = $this->course_module[0]->modname;
$grade_item->iteminstance = $this->course_module[0]->instance;
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 30;
$grade_item->grademax = 110;
$grade_item->itemnumber = 1;
$grade_item->idnumber = 'item id 0';
$grade_item->iteminfo = 'Grade item 0 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 3;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[0] = $grade_item;
// id = 1
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[1]->id;
$grade_item->itemname = 'unittestgradeitem2';
$grade_item->itemtype = 'import';
$grade_item->itemmodule = $this->course_module[1]->modname;
$grade_item->iteminstance = $this->course_module[1]->instance;
$grade_item->calculation = '= ##gi'.$this->grade_items[0]->id.'## + 30 + [[item id 0]] - [[item id 0]]';
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->itemnumber = null;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 1 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 4;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[1] = $grade_item;
// id = 2
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[2]->id;
$grade_item->itemname = 'unittestgradeitem3';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = $this->course_module[2]->modname;
$grade_item->iteminstance = $this->course_module[2]->instance;
$grade_item->gradetype = GRADE_TYPE_SCALE;
$grade_item->scaleid = $this->scale[0]->id;
$grade_item->grademin = 0;
$grade_item->grademax = $this->scalemax[0];
$grade_item->iteminfo = 'Grade item 2 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 6;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[2] = $grade_item;
// Load grade_items associated with the 3 categories.
// id = 3
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->iteminstance = $this->grade_categories[0]->id;
$grade_item->itemname = 'unittestgradeitemcategory1';
$grade_item->needsupdate = 0;
$grade_item->itemtype = 'category';
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 3 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 1;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[3] = $grade_item;
// id = 4
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->iteminstance = $this->grade_categories[1]->id;
$grade_item->itemname = 'unittestgradeitemcategory2';
$grade_item->itemtype = 'category';
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->needsupdate = 0;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 4 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 2;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[4] = $grade_item;
// id = 5
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->iteminstance = $this->grade_categories[2]->id;
$grade_item->itemname = 'unittestgradeitemcategory3';
$grade_item->itemtype = 'category';
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->needsupdate = true;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 5 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 5;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[5] = $grade_item;
// Orphan grade_item.
// id = 6
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $course_category->id;
$grade_item->itemname = 'unittestorphangradeitem1';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = $this->course_module[4]->modname;
$grade_item->iteminstance = $this->course_module[4]->instance;
$grade_item->itemnumber = 0;
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 10;
$grade_item->grademax = 120;
$grade_item->locked = time();
$grade_item->iteminfo = 'Orphan Grade 6 item used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 7;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[6] = $grade_item;
// 2 grade items under level1category.
// id = 7
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[3]->id;
$grade_item->itemname = 'singleparentitem1';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = $this->course_module[5]->modname;
$grade_item->iteminstance = $this->course_module[5]->instance;
$grade_item->gradetype = GRADE_TYPE_SCALE;
$grade_item->scaleid = $this->scale[0]->id;
$grade_item->grademin = 0;
$grade_item->grademax = $this->scalemax[0];
$grade_item->iteminfo = 'Grade item 7 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 9;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[7] = $grade_item;
// id = 8
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[3]->id;
$grade_item->itemname = 'singleparentitem2';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = $this->course_module[6]->modname;
$grade_item->iteminstance = $this->course_module[6]->instance;
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 8 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 10;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[8] = $grade_item;
// Grade_item for level1category.
// id = 9
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->itemname = 'grade_item for level1 category';
$grade_item->itemtype = 'category';
$grade_item->iteminstance = $this->grade_categories[3]->id;
$grade_item->needsupdate = true;
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Orphan Grade item 9 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 8;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[9] = $grade_item;
// Manual grade_item.
// id = 10
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $course_category->id;
$grade_item->itemname = 'manual grade_item';
$grade_item->itemtype = 'manual';
$grade_item->itemnumber = 0;
$grade_item->needsupdate = false;
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Manual grade item 10 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 10;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[10] = $grade_item;
// Quiz grade_item (course_module = 7).
// id = 11
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $course_category->id;
$grade_item->itemname = 'Quiz grade item';
$grade_item->itemtype = 'mod';
$grade_item->itemmodule = $this->course_module[7]->modname;
$grade_item->iteminstance = $this->course_module[7]->instance;
$grade_item->itemnumber = 0;
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->locked = 0;
$grade_item->iteminfo = 'Quiz grade item used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 11;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[11] = $grade_item;
// id = 12
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->iteminstance = $this->grade_categories[4]->id;
$grade_item->itemname = 'unittestgradeitemcategory7';
$grade_item->itemtype = 'category';
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->needsupdate = true;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 12 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 12;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[12] = $grade_item;
// id = 13
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->iteminstance = $this->grade_categories[5]->id;
$grade_item->itemname = 'unittestgradeitemcategory5';
$grade_item->itemtype = 'category';
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->needsupdate = true;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 13 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 13;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[13] = $grade_item;
// id = 14
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->iteminstance = $this->grade_categories[6]->id;
$grade_item->itemname = 'unittestgradeitemcategory6';
$grade_item->itemtype = 'category';
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->needsupdate = true;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Grade item 14 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 14;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[14] = $grade_item;
// Manual grade_item
// id = 15
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[5]->id;
$grade_item->itemname = 'manual grade_item';
$grade_item->itemtype = 'manual';
$grade_item->itemnumber = 0;
$grade_item->needsupdate = false;
$grade_item->gradetype = GRADE_TYPE_VALUE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Manual grade item 15 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 15;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[15] = $grade_item;
// Manual grade_item
// id = 16
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[6]->id;
$grade_item->itemname = 'manual grade_item';
$grade_item->itemtype = 'manual';
$grade_item->itemnumber = 0;
$grade_item->needsupdate = false;
$grade_item->gradetype = GRADE_TYPE_SCALE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Manual grade item 16 used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->sortorder = 16;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[16] = $grade_item;
// $this->grade_items[17] loaded in load_grade_outcomes() in order to use an outcome id.
}
/**
* Load grade_grades data into the database, and adds the corresponding objects to this class' variable.
*/
private function load_grade_grades() {
global $DB;
// This method is called once for each test method. Avoid adding things to $this->grade_grades multiple times.
$this->grade_grades = array();
// Grades for grade_item 1.
$grade = new stdClass();
$grade->itemid = $this->grade_items[0]->id;
$grade->userid = $this->user[1]->id;
$grade->rawgrade = 15; // too small
$grade->finalgrade = 30;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '1 of 17 grade_grades';
$grade->informationformat = FORMAT_PLAIN;
$grade->feedback = 'Good, but not good enough..';
$grade->feedbackformat = FORMAT_PLAIN;
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[0] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[0]->id;
$grade->userid = $this->user[2]->id;
$grade->rawgrade = 40;
$grade->finalgrade = 40;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '2 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[1] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[0]->id;
$grade->userid = $this->user[3]->id;
$grade->rawgrade = 170; // too big
$grade->finalgrade = 110;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '3 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[2] = $grade;
// No raw grades for grade_item 2 - it is calculated.
$grade = new stdClass();
$grade->itemid = $this->grade_items[1]->id;
$grade->userid = $this->user[1]->id;
$grade->finalgrade = 60;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '4 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[3] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[1]->id;
$grade->userid = $this->user[2]->id;
$grade->finalgrade = 70;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '5 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[4] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[1]->id;
$grade->userid = $this->user[3]->id;
$grade->finalgrade = 100;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '6 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[5] = $grade;
// Grades for grade_item 3.
$grade = new stdClass();
$grade->itemid = $this->grade_items[2]->id;
$grade->userid = $this->user[1]->id;
$grade->rawgrade = 2;
$grade->finalgrade = 6;
$grade->scaleid = $this->scale[3]->id;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '7 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[6] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[2]->id;
$grade->userid = $this->user[2]->id;
$grade->rawgrade = 3;
$grade->finalgrade = 2;
$grade->scaleid = $this->scale[3]->id;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '8 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[2]->id;
$grade->userid = $this->user[3]->id;
$grade->rawgrade = 1;
$grade->finalgrade = 3;
$grade->scaleid = $this->scale[3]->id;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '9 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
// Grades for grade_item 7.
$grade = new stdClass();
$grade->itemid = $this->grade_items[6]->id;
$grade->userid = $this->user[1]->id;
$grade->rawgrade = 97;
$grade->finalgrade = 69;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '10 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[6]->id;
$grade->userid = $this->user[2]->id;
$grade->rawgrade = 49;
$grade->finalgrade = 87;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '11 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[6]->id;
$grade->userid = $this->user[3]->id;
$grade->rawgrade = 67;
$grade->finalgrade = 94;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '12 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
// Grades for grade_item 8.
$grade = new stdClass();
$grade->itemid = $this->grade_items[7]->id;
$grade->userid = $this->user[2]->id;
$grade->rawgrade = 3;
$grade->finalgrade = 3;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '13 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[7]->id;
$grade->userid = $this->user[3]->id;
$grade->rawgrade = 6;
$grade->finalgrade = 6;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '14 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
// Grades for grade_item 9.
$grade = new stdClass();
$grade->itemid = $this->grade_items[8]->id;
$grade->userid = $this->user[1]->id;
$grade->rawgrade = 20;
$grade->finalgrade = 20;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '15 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[8]->id;
$grade->userid = $this->user[2]->id;
$grade->rawgrade = 50;
$grade->finalgrade = 50;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '16 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
$grade = new stdClass();
$grade->itemid = $this->grade_items[8]->id;
$grade->userid = $this->user[3]->id;
$grade->rawgrade = 100;
$grade->finalgrade = 100;
$grade->timecreated = time();
$grade->timemodified = time();
$grade->information = '17 of 17 grade_grades';
$grade->id = $DB->insert_record('grade_grades', $grade);
$this->grade_grades[] = $grade;
}
/**
* Load grade_outcome data into the database, and adds the corresponding objects to this class' variable.
*/
private function load_grade_outcomes() {
global $DB;
// This method is called once for each test method. Avoid adding things to $this->grade_outcomes multiple times.
$this->grade_outcomes = array();
// Calculation for grade_item 1.
$grade_outcome = new stdClass();
$grade_outcome->fullname = 'Team work';
$grade_outcome->shortname = 'Team work';
$grade_outcome->fullname = 'Team work outcome';
$grade_outcome->timecreated = time();
$grade_outcome->timemodified = time();
$grade_outcome->scaleid = $this->scale[2]->id;
$grade_outcome->id = $DB->insert_record('grade_outcomes', $grade_outcome);
$this->grade_outcomes[] = $grade_outcome;
// Calculation for grade_item 2.
$grade_outcome = new stdClass();
$grade_outcome->fullname = 'Complete circuit board';
$grade_outcome->shortname = 'Complete circuit board';
$grade_outcome->fullname = 'Complete circuit board';
$grade_outcome->timecreated = time();
$grade_outcome->timemodified = time();
$grade_outcome->scaleid = $this->scale[3]->id;
$grade_outcome->id = $DB->insert_record('grade_outcomes', $grade_outcome);
$this->grade_outcomes[] = $grade_outcome;
// Calculation for grade_item 3.
$grade_outcome = new stdClass();
$grade_outcome->fullname = 'Debug Java program';
$grade_outcome->shortname = 'Debug Java program';
$grade_outcome->fullname = 'Debug Java program';
$grade_outcome->timecreated = time();
$grade_outcome->timemodified = time();
$grade_outcome->scaleid = $this->scale[4]->id;
$grade_outcome->id = $DB->insert_record('grade_outcomes', $grade_outcome);
$this->grade_outcomes[] = $grade_outcome;
// Manual grade_item with outcome
// id = 17
$grade_item = new stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[6]->id;
$grade_item->itemname = 'manual grade_item';
$grade_item->itemtype = 'manual';
$grade_item->itemnumber = 0;
$grade_item->needsupdate = false;
$grade_item->gradetype = GRADE_TYPE_SCALE;
$grade_item->grademin = 0;
$grade_item->grademax = 100;
$grade_item->iteminfo = 'Manual grade item 16 with outcome used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item->outcomeid = $this->grade_outcomes[2]->id;
$grade_item->sortorder = 17;
$grade_item->id = $DB->insert_record('grade_items', $grade_item);
$this->grade_items[17] = $grade_item;
}
}
/**
* Allow calling protected method.
*/
class test_grade_grade_flatten_dependencies_array extends grade_grade {
public static function test_flatten_dependencies_array(&$a,&$b) {
return self::flatten_dependencies_array($a, $b);
}
}
+923
View File
@@ -0,0 +1,923 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/fixtures/lib.php');
/**
* Test grade categories
*
* @package core
* @category test
* @copyright nicolas@moodle.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_category_test extends \grade_base_testcase {
public function test_grade_category(): void {
$this->sub_test_grade_category_construct();
$this->sub_test_grade_category_build_path();
$this->sub_test_grade_category_fetch();
$this->sub_test_grade_category_fetch_all();
$this->sub_test_grade_category_update();
$this->sub_test_grade_category_delete();
$this->sub_test_grade_category_insert();
$this->sub_test_grade_category_qualifies_for_regrading();
$this->sub_test_grade_category_force_regrading();
$this->sub_test_grade_category_aggregate_grades();
$this->sub_test_grade_category_apply_limit_rules();
$this->sub_test_grade_category_is_aggregationcoef_used();
$this->sub_test_grade_category_aggregation_uses_aggregationcoef();
$this->sub_test_grade_category_fetch_course_tree();
$this->sub_test_grade_category_get_children();
$this->sub_test_grade_category_load_grade_item();
$this->sub_test_grade_category_get_grade_item();
$this->sub_test_grade_category_load_parent_category();
$this->sub_test_grade_category_get_parent_category();
$this->sub_test_grade_category_get_name_escaped();
$this->sub_test_grade_category_get_name_unescaped();
$this->sub_test_grade_category_generate_grades_aggregationweight();
$this->sub_test_grade_category_set_parent();
$this->sub_test_grade_category_get_final();
$this->sub_test_grade_category_get_sortorder();
$this->sub_test_grade_category_set_sortorder();
$this->sub_test_grade_category_is_editable();
$this->sub_test_grade_category_move_after_sortorder();
$this->sub_test_grade_category_is_course_category();
$this->sub_test_grade_category_fetch_course_category();
$this->sub_test_grade_category_is_locked();
$this->sub_test_grade_category_set_locked();
$this->sub_test_grade_category_is_hidden();
$this->sub_test_grade_category_set_hidden();
$this->sub_test_grade_category_can_control_visibility();
$this->sub_test_grade_category_total_visibility();
// This won't work until MDL-11837 is complete.
// $this->sub_test_grade_category_generate_grades();
// Do this last as adding a second course category messes up the data.
$this->sub_test_grade_category_insert_course_category();
$this->sub_test_grade_category_is_extracredit_used();
$this->sub_test_grade_category_aggregation_uses_extracredit();
}
// Adds 3 new grade categories at various depths.
protected function sub_test_grade_category_construct() {
$course_category = \grade_category::fetch_course_category($this->courseid);
$params = new \stdClass();
$params->courseid = $this->courseid;
$params->fullname = 'unittestcategory4';
$grade_category = new \grade_category($params, false);
$grade_category->insert();
$this->grade_categories[] = $grade_category;
$this->assertEquals($params->courseid, $grade_category->courseid);
$this->assertEquals($params->fullname, $grade_category->fullname);
$this->assertEquals(2, $grade_category->depth);
$this->assertEquals("/$course_category->id/$grade_category->id/", $grade_category->path);
$parentpath = $grade_category->path;
// Test a child category.
$params->parent = $grade_category->id;
$params->fullname = 'unittestcategory5';
$grade_category = new \grade_category($params, false);
$grade_category->insert();
$this->grade_categories[] = $grade_category;
$this->assertEquals(3, $grade_category->depth);
$this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path);
$parentpath = $grade_category->path;
// Test a third depth category.
$params->parent = $grade_category->id;
$params->fullname = 'unittestcategory6';
$grade_category = new \grade_category($params, false);
$grade_category->insert();
$this->grade_categories[50] = $grade_category;// Going to delete this one later hence the special index.
$this->assertEquals(4, $grade_category->depth);
$this->assertEquals($parentpath.$grade_category->id."/", $grade_category->path);
}
protected function sub_test_grade_category_build_path() {
$grade_category = new \grade_category($this->grade_categories[1]);
$this->assertTrue(method_exists($grade_category, 'build_path'));
$path = \grade_category::build_path($grade_category);
$this->assertEquals($grade_category->path, $path);
}
protected function sub_test_grade_category_fetch() {
$grade_category = new \grade_category();
$this->assertTrue(method_exists($grade_category, 'fetch'));
$grade_category = \grade_category::fetch(array('id'=>$this->grade_categories[0]->id));
$this->assertEquals($this->grade_categories[0]->id, $grade_category->id);
$this->assertEquals($this->grade_categories[0]->fullname, $grade_category->fullname);
}
protected function sub_test_grade_category_fetch_all() {
$grade_category = new \grade_category();
$this->assertTrue(method_exists($grade_category, 'fetch_all'));
$grade_categories = \grade_category::fetch_all(array('courseid'=>$this->courseid));
$this->assertEquals(count($this->grade_categories), count($grade_categories)-1);
}
protected function sub_test_grade_category_update() {
global $DB;
$grade_category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($grade_category, 'update'));
$grade_category->fullname = 'Updated info for this unittest grade_category';
$grade_category->path = null; // Path must be recalculated if missing.
$grade_category->depth = null;
$grade_category->aggregation = GRADE_AGGREGATE_MAX; // Should force regrading.
$grade_item = $grade_category->get_grade_item();
$this->assertEquals(0, $grade_item->needsupdate);
$this->assertTrue($grade_category->update());
$fullname = $DB->get_field('grade_categories', 'fullname', array('id' => $this->grade_categories[0]->id));
$this->assertEquals($grade_category->fullname, $fullname);
$path = $DB->get_field('grade_categories', 'path', array('id' => $this->grade_categories[0]->id));
$this->assertEquals($grade_category->path, $path);
$depth = $DB->get_field('grade_categories', 'depth', array('id' => $this->grade_categories[0]->id));
$this->assertEquals($grade_category->depth, $depth);
$grade_item = $grade_category->get_grade_item();
$this->assertEquals(1, $grade_item->needsupdate);
}
protected function sub_test_grade_category_delete() {
global $DB;
$grade_category = new \grade_category($this->grade_categories[50]);
$this->assertTrue(method_exists($grade_category, 'delete'));
$this->assertTrue($grade_category->delete());
$this->assertFalse($DB->get_record('grade_categories', array('id' => $grade_category->id)));
}
protected function sub_test_grade_category_insert() {
$course_category = \grade_category::fetch_course_category($this->courseid);
$grade_category = new \grade_category();
$this->assertTrue(method_exists($grade_category, 'insert'));
$grade_category->fullname = 'unittestcategory4';
$grade_category->courseid = $this->courseid;
$grade_category->aggregation = GRADE_AGGREGATE_MEAN;
$grade_category->aggregateonlygraded = 1;
$grade_category->keephigh = 100;
$grade_category->droplow = 10;
$grade_category->hidden = 0;
$grade_category->parent = $this->grade_categories[1]->id; // sub_test_grade_category_delete() removed the category at 0.
$grade_category->insert();
$this->assertEquals('/'.$course_category->id.'/'.$this->grade_categories[1]->parent.'/'.$this->grade_categories[1]->id.'/'.$grade_category->id.'/', $grade_category->path);
$this->assertEquals(4, $grade_category->depth);
$last_grade_category = end($this->grade_categories);
$this->assertFalse(empty($grade_category->grade_item));
$this->assertEquals($grade_category->id, $grade_category->grade_item->iteminstance);
$this->assertEquals('category', $grade_category->grade_item->itemtype);
$this->assertEquals($grade_category->id, $last_grade_category->id + 1);
$this->assertFalse(empty($grade_category->timecreated));
$this->assertFalse(empty($grade_category->timemodified));
}
protected function sub_test_grade_category_qualifies_for_regrading() {
$grade_category = new \grade_category($this->grade_categories[1]);
$this->assertTrue(method_exists($grade_category, 'qualifies_for_regrading'));
$this->assertFalse($grade_category->qualifies_for_regrading());
$grade_category->aggregation = GRADE_AGGREGATE_MAX;
$this->assertTrue($grade_category->qualifies_for_regrading());
$grade_category = new \grade_category($this->grade_categories[1]);
$grade_category->droplow = 99;
$this->assertTrue($grade_category->qualifies_for_regrading());
$grade_category = new \grade_category($this->grade_categories[1]);
$grade_category->keephigh = 99;
$this->assertTrue($grade_category->qualifies_for_regrading());
}
protected function sub_test_grade_category_force_regrading() {
$grade_category = new \grade_category($this->grade_categories[1]);
$this->assertTrue(method_exists($grade_category, 'force_regrading'));
$grade_category->load_grade_item();
$this->assertEquals(0, $grade_category->grade_item->needsupdate);
$grade_category->force_regrading();
$grade_category->grade_item = null;
$grade_category->load_grade_item();
$this->assertEquals(1, $grade_category->grade_item->needsupdate);
}
/**
* Tests the setting of the grade_grades aggregationweight column.
* Currently, this is only a regression test for MDL-51715.
* This must be run before sub_test_grade_category_set_parent(), which alters
* the fixture.
*/
protected function sub_test_grade_category_generate_grades_aggregationweight() {
global $DB;
// Start of regression test for MDL-51715.
// grade_categories [1] and [2] are child categories of [0]
// Ensure that grades have been generated with fixture data.
$childcat1 = new \grade_category($this->grade_categories[1]);
$childcat1itemid = $childcat1->load_grade_item()->id;
$childcat1->generate_grades();
$childcat2 = new \grade_category($this->grade_categories[2]);
$childcat2itemid = $childcat2->load_grade_item()->id;
$childcat2->generate_grades();
$parentcat = new \grade_category($this->grade_categories[0]);
$parentcat->generate_grades();
// Drop low and and re-generate to produce 'dropped' aggregation status.
$parentcat->droplow = 1;
$parentcat->generate_grades();
$this->assertTrue($DB->record_exists_select(
'grade_grades',
"aggregationstatus='dropped' and itemid in (?,?)",
array($childcat1itemid, $childcat2itemid)));
$this->assertFalse($DB->record_exists_select(
'grade_grades',
"aggregationstatus='dropped' and aggregationweight > 0.00"),
"aggregationweight should be 0.00 if aggregationstatus=='dropped'");
// Reset grade data to be consistent with fixture data.
$parentcat->droplow = 0;
$parentcat->generate_grades();
// Blank out the final grade for one of the child categories and re-generate
// to produce 'novalue' aggregationstatus. Direct DB update is testing shortcut.
$DB->set_field('grade_grades', 'finalgrade', null, array('itemid'=>$childcat1itemid));
$parentcat->generate_grades();
$this->assertFalse($DB->record_exists_select(
'grade_grades',
"aggregationstatus='dropped' and itemid in (?,?)",
array($childcat1itemid, $childcat2itemid)));
$this->assertTrue($DB->record_exists_select(
'grade_grades',
"aggregationstatus='novalue' and itemid = ?",
array($childcat1itemid)));
$this->assertFalse($DB->record_exists_select(
'grade_grades',
"aggregationstatus='novalue' and aggregationweight > 0.00"),
"aggregationweight should be 0.00 if aggregationstatus=='novalue'");
// Re-generate to be consistent with fixture data.
$childcat1->generate_grades();
$parentcat->generate_grades();
// End of regression test for MDL-51715.
}
/**
* Tests the calculation of grades using the various aggregation methods with and without hidden grades
* This will not work entirely until MDL-11837 is done
*/
protected function sub_test_grade_category_generate_grades() {
global $DB;
// Inserting some special grade items to make testing the final grade calculation easier.
$params = new \stdClass();
$params->courseid = $this->courseid;
$params->fullname = 'unittestgradecalccategory';
$params->aggregation = GRADE_AGGREGATE_MEAN;
$params->aggregateonlygraded = 0;
$grade_category = new \grade_category($params, false);
$grade_category->insert();
$this->assertTrue(method_exists($grade_category, 'generate_grades'));
$grade_category->load_grade_item();
$cgi = $grade_category->get_grade_item();
$cgi->grademin = 0;
$cgi->grademax = 20; // 3 grade items out of 10 but category is out of 20 to force scaling to occur.
$cgi->update();
// 3 grade items each with a maximum grade of 10.
$grade_items = array();
for ($i=0; $i<3; $i++) {
$grade_items[$i] = new \grade_item();
$grade_items[$i]->courseid = $this->courseid;
$grade_items[$i]->categoryid = $grade_category->id;
$grade_items[$i]->itemname = 'manual grade_item '.$i;
$grade_items[$i]->itemtype = 'manual';
$grade_items[$i]->itemnumber = 0;
$grade_items[$i]->needsupdate = false;
$grade_items[$i]->gradetype = GRADE_TYPE_VALUE;
$grade_items[$i]->grademin = 0;
$grade_items[$i]->grademax = 10;
$grade_items[$i]->iteminfo = 'Manual grade item used for unit testing';
$grade_items[$i]->timecreated = time();
$grade_items[$i]->timemodified = time();
// Used as the weight by weighted mean and as extra credit by mean with extra credit.
// Will be 0, 1 and 2.
$grade_items[$i]->aggregationcoef = $i;
$grade_items[$i]->insert();
}
// A grade for each grade item.
$grade_grades = array();
for ($i=0; $i<3; $i++) {
$grade_grades[$i] = new \grade_grade();
$grade_grades[$i]->itemid = $grade_items[$i]->id;
$grade_grades[$i]->userid = $this->userid;
$grade_grades[$i]->rawgrade = ($i+1)*2; // Produce grade grades of 2, 4 and 6.
$grade_grades[$i]->finalgrade = ($i+1)*2;
$grade_grades[$i]->timecreated = time();
$grade_grades[$i]->timemodified = time();
$grade_grades[$i]->information = '1 of 2 grade_grades';
$grade_grades[$i]->informationformat = FORMAT_PLAIN;
$grade_grades[$i]->feedback = 'Good, but not good enough..';
$grade_grades[$i]->feedbackformat = FORMAT_PLAIN;
$grade_grades[$i]->insert();
}
// 3 grade items with 1 grade_grade each.
// grade grades have the values 2, 4 and 6.
// First correct answer is the aggregate with all 3 grades.
// Second correct answer is with the first grade (value 2) hidden.
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEDIAN, 'GRADE_AGGREGATE_MEDIAN', 8, 8);
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MAX, 'GRADE_AGGREGATE_MAX', 12, 12);
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MODE, 'GRADE_AGGREGATE_MODE', 12, 12);
// Weighted mean. note grade totals are rounded to an int to prevent rounding discrepancies. correct final grade isnt actually exactly 10
// 3 items with grades 2, 4 and 6 with weights 0, 1 and 2 and all out of 10. then doubled to be out of 20.
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN, 'GRADE_AGGREGATE_WEIGHTED_MEAN', 10, 10);
// Simple weighted mean.
// 3 items with grades 2, 4 and 6 equally weighted and all out of 10. then doubled to be out of 20.
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_WEIGHTED_MEAN2, 'GRADE_AGGREGATE_WEIGHTED_MEAN2', 8, 10);
// Mean of grades with extra credit.
// 3 items with grades 2, 4 and 6 with extra credit 0, 1 and 2 equally weighted and all out of 10. then doubled to be out of 20.
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_EXTRACREDIT_MEAN, 'GRADE_AGGREGATE_EXTRACREDIT_MEAN', 10, 13);
// Aggregation tests the are affected by a hidden grade currently dont work as we dont store the altered grade in the database
// instead an in memory recalculation is done. This should be remedied by MDL-11837.
// Fails with 1 grade hidden. still reports 8 as being correct.
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MEAN, 'GRADE_AGGREGATE_MEAN', 8, 10);
// Fails with 1 grade hidden. still reports 4 as being correct.
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_MIN, 'GRADE_AGGREGATE_MIN', 4, 8);
// Fails with 1 grade hidden. still reports 12 as being correct.
$this->helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, GRADE_AGGREGATE_SUM, 'GRADE_AGGREGATE_SUM', 12, 10);
}
/**
* Test grade category aggregation using the supplied grade objects and aggregation method
* @param \grade_category $grade_category the category to be tested
* @param array $grade_items array of instance of grade_item
* @param array $grade_grades array of instances of grade_grade
* @param int $aggmethod the aggregation method to apply ie GRADE_AGGREGATE_MEAN
* @param string $aggmethodname the name of the aggregation method to apply. Used to display any test failure messages
* @param int $correct1 the correct final grade for the category with NO items hidden
* @param int $correct2 the correct final grade for the category with the grade at $grade_grades[0] hidden
* @return void
*/
protected function helper_test_grade_agg_method($grade_category, $grade_items, $grade_grades, $aggmethod, $aggmethodname, $correct1, $correct2) {
$grade_category->aggregation = $aggmethod;
$grade_category->update();
// Check grade_item isnt hidden from a previous test.
$grade_items[0]->set_hidden(0, true);
$this->helper_test_grade_aggregation_result($grade_category, $correct1, 'Testing aggregation method('.$aggmethodname.') with no items hidden %s');
// Hide the grade item with grade of 2.
$grade_items[0]->set_hidden(1, true);
$this->helper_test_grade_aggregation_result($grade_category, $correct2, 'Testing aggregation method('.$aggmethodname.') with 1 item hidden %s');
}
/**
* Verify the value of the category grade item for $this->userid
* @param \grade_category $grade_category the category to be tested
* @param int $correctgrade the expected grade
* @param string $msg The message that should be displayed if the correct grade is not found
* @return void
*/
protected function helper_test_grade_aggregation_result($grade_category, $correctgrade, $msg) {
global $DB;
$category_grade_item = $grade_category->get_grade_item();
// This creates all the grade_grades we need.
grade_regrade_final_grades($this->courseid);
$grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid));
$this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax);
$this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg);
/*
* TODO this doesnt work as the grade_grades created by $grade_category->generate_grades(); dont
* observe the category's max grade
// delete the grade_grades for the category itself and check they get recreated correctly.
$DB->delete_records('grade_grades', array('itemid'=>$category_grade_item->id));
$grade_category->generate_grades();
$grade = $DB->get_record('grade_grades', array('itemid'=>$category_grade_item->id, 'userid'=>$this->userid));
$this->assertWithinMargin($grade->rawgrade, $grade->rawgrademin, $grade->rawgrademax);
$this->assertEquals(intval($correctgrade), intval($grade->finalgrade), $msg);
*
*/
}
protected function sub_test_grade_category_aggregate_grades() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'aggregate_grades'));
// Tested more fully via test_grade_category_generate_grades().
}
protected function sub_test_grade_category_apply_limit_rules() {
$items[$this->grade_items[0]->id] = new \grade_item($this->grade_items[0], false);
$items[$this->grade_items[1]->id] = new \grade_item($this->grade_items[1], false);
$items[$this->grade_items[2]->id] = new \grade_item($this->grade_items[2], false);
$items[$this->grade_items[4]->id] = new \grade_item($this->grade_items[4], false);
// Test excluding the lowest 2 out of 4 grades from aggregation with no 0 grades.
$category = new \grade_category();
$category->droplow = 2;
$grades = array($this->grade_items[0]->id=>5.374,
$this->grade_items[1]->id=>9.4743,
$this->grade_items[2]->id=>2.5474,
$this->grade_items[4]->id=>7.3754);
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 2);
$this->assertEquals($grades[$this->grade_items[1]->id], 9.4743);
$this->assertEquals($grades[$this->grade_items[4]->id], 7.3754);
// Test aggregating only the highest 1 out of 4 grades.
$category = new \grade_category();
$category->keephigh = 1;
$category->droplow = 0;
$grades = array($this->grade_items[0]->id=>5.374,
$this->grade_items[1]->id=>9.4743,
$this->grade_items[2]->id=>2.5474,
$this->grade_items[4]->id=>7.3754);
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 1);
$grade = reset($grades);
$this->assertEquals(9.4743, $grade);
// Test excluding the lowest 2 out of 4 grades from aggregation with no 0 grades.
// An extra credit grade item should be kept even if droplow means it would otherwise be excluded.
$category = new \grade_category();
$category->droplow = 2;
$category->aggregation = GRADE_AGGREGATE_SUM;
$items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit".
$grades = array($this->grade_items[0]->id=>5.374,
$this->grade_items[1]->id=>9.4743,
$this->grade_items[2]->id=>2.5474,
$this->grade_items[4]->id=>7.3754);
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 2);
$this->assertEquals($grades[$this->grade_items[1]->id], 9.4743);
$this->assertEquals($grades[$this->grade_items[2]->id], 2.5474);
// Test only aggregating the highest 1 out of 4 grades.
// An extra credit grade item is retained in addition to the highest grade.
$category = new \grade_category();
$category->keephigh = 1;
$category->droplow = 0;
$category->aggregation = GRADE_AGGREGATE_SUM;
$items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit".
$grades = array($this->grade_items[0]->id=>5.374,
$this->grade_items[1]->id=>9.4743,
$this->grade_items[2]->id=>2.5474,
$this->grade_items[4]->id=>7.3754);
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 2);
$this->assertEquals($grades[$this->grade_items[1]->id], 9.4743);
$this->assertEquals($grades[$this->grade_items[2]->id], 2.5474);
// Test excluding the lowest 1 out of 4 grades from aggregation with two 0 grades.
$items[$this->grade_items[2]->id]->aggregationcoef = 0; // Undo marking grade item 2 as "extra credit".
$category = new \grade_category();
$category->droplow = 1;
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean.
$grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
$this->grade_items[1]->id=>5, // 5 out of 100.
$this->grade_items[2]->id=>2, // 0 out of 6.
$this->grade_items[4]->id=>0); // 0 out of 100.
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 3);
$this->assertEquals($grades[$this->grade_items[1]->id], 5);
$this->assertEquals($grades[$this->grade_items[2]->id], 2);
$this->assertEquals($grades[$this->grade_items[4]->id], 0);
// Test excluding the lowest 2 out of 4 grades from aggregation with three 0 grades.
$category = new \grade_category();
$category->droplow = 2;
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean.
$grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
$this->grade_items[1]->id=>5, // 5 out of 100.
$this->grade_items[2]->id=>0, // 0 out of 6.
$this->grade_items[4]->id=>0); // 0 out of 100. Should be excluded from aggregation.
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 2);
$this->assertEquals($grades[$this->grade_items[1]->id], 5);
$this->assertEquals($grades[$this->grade_items[2]->id], 0);
// Test excluding the lowest 5 out of 4 grades from aggregation.
// Just to check we handle this sensibly.
$category = new \grade_category();
$category->droplow = 5;
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean.
$grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
$this->grade_items[1]->id=>5, // 5 out of 100.
$this->grade_items[2]->id=>6, // 6 out of 6.
$this->grade_items[4]->id=>1);// 1 out of 100. Should be excluded from aggregation.
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 0);
// Test excluding the lowest 4 out of 4 grades from aggregation with one marked as extra credit.
$category = new \grade_category();
$category->droplow = 4;
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean.
$items[$this->grade_items[2]->id]->aggregationcoef = 1; // Mark grade item 2 as "extra credit".
$grades = array($this->grade_items[0]->id=>0, // 0 out of 110. Should be excluded from aggregation.
$this->grade_items[1]->id=>5, // 5 out of 100. Should be excluded from aggregation.
$this->grade_items[2]->id=>6, // 6 out of 6. Extra credit. Should be retained.
$this->grade_items[4]->id=>1);// 1 out of 100. Should be excluded from aggregation.
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 1);
$this->assertEquals($grades[$this->grade_items[2]->id], 6);
// MDL-35667 - There was an infinite loop if several items had the same grade and at least one was extra credit.
$category = new \grade_category();
$category->droplow = 1;
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2; // Simple weighted mean.
$items[$this->grade_items[1]->id]->aggregationcoef = 1; // Mark grade item 1 as "extra credit".
$grades = array($this->grade_items[0]->id=>1, // 1 out of 110. Should be excluded from aggregation.
$this->grade_items[1]->id=>1, // 1 out of 100. Extra credit. Should be retained.
$this->grade_items[2]->id=>1, // 1 out of 6. Should be retained.
$this->grade_items[4]->id=>1);// 1 out of 100. Should be retained.
$category->apply_limit_rules($grades, $items);
$this->assertEquals(count($grades), 3);
$this->assertEquals($grades[$this->grade_items[1]->id], 1);
$this->assertEquals($grades[$this->grade_items[2]->id], 1);
$this->assertEquals($grades[$this->grade_items[4]->id], 1);
}
protected function sub_test_grade_category_is_aggregationcoef_used() {
$category = new \grade_category();
// Following use aggregationcoef.
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN;
$this->assertTrue($category->is_aggregationcoef_used());
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2;
$this->assertTrue($category->is_aggregationcoef_used());
$category->aggregation = GRADE_AGGREGATE_EXTRACREDIT_MEAN;
$this->assertTrue($category->is_aggregationcoef_used());
$category->aggregation = GRADE_AGGREGATE_SUM;
$this->assertTrue($category->is_aggregationcoef_used());
// Following don't use aggregationcoef.
$category->aggregation = GRADE_AGGREGATE_MAX;
$this->assertFalse($category->is_aggregationcoef_used());
$category->aggregation = GRADE_AGGREGATE_MEAN;
$this->assertFalse($category->is_aggregationcoef_used());
$category->aggregation = GRADE_AGGREGATE_MEDIAN;
$this->assertFalse($category->is_aggregationcoef_used());
$category->aggregation = GRADE_AGGREGATE_MIN;
$this->assertFalse($category->is_aggregationcoef_used());
$category->aggregation = GRADE_AGGREGATE_MODE;
$this->assertFalse($category->is_aggregationcoef_used());
}
protected function sub_test_grade_category_aggregation_uses_aggregationcoef() {
$this->assertTrue(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_WEIGHTED_MEAN));
$this->assertTrue(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_WEIGHTED_MEAN2));
$this->assertTrue(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_EXTRACREDIT_MEAN));
$this->assertTrue(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_SUM));
$this->assertFalse(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MAX));
$this->assertFalse(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MEAN));
$this->assertFalse(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MEDIAN));
$this->assertFalse(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MIN));
$this->assertFalse(\grade_category::aggregation_uses_aggregationcoef(GRADE_AGGREGATE_MODE));
}
protected function sub_test_grade_category_fetch_course_tree() {
$category = new \grade_category();
$this->assertTrue(method_exists($category, 'fetch_course_tree'));
// TODO: add some tests.
}
protected function sub_test_grade_category_get_children() {
$course_category = \grade_category::fetch_course_category($this->courseid);
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'get_children'));
$children_array = $category->get_children(0);
$this->assertTrue(is_array($children_array));
$this->assertFalse(empty($children_array[2]));
$this->assertFalse(empty($children_array[2]['object']));
$this->assertFalse(empty($children_array[2]['children']));
$this->assertEquals($this->grade_categories[1]->id, $children_array[2]['object']->id);
$this->assertEquals($this->grade_categories[2]->id, $children_array[5]['object']->id);
$this->assertEquals($this->grade_items[0]->id, $children_array[2]['children'][3]['object']->id);
$this->assertEquals($this->grade_items[1]->id, $children_array[2]['children'][4]['object']->id);
$this->assertEquals($this->grade_items[2]->id, $children_array[5]['children'][6]['object']->id);
}
protected function sub_test_grade_category_load_grade_item() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'load_grade_item'));
$this->assertEquals(null, $category->grade_item);
$category->load_grade_item();
$this->assertEquals($this->grade_items[3]->id, $category->grade_item->id);
}
protected function sub_test_grade_category_get_grade_item() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'get_grade_item'));
$grade_item = $category->get_grade_item();
$this->assertEquals($this->grade_items[3]->id, $grade_item->id);
}
protected function sub_test_grade_category_load_parent_category() {
$category = new \grade_category($this->grade_categories[1]);
$this->assertTrue(method_exists($category, 'load_parent_category'));
$this->assertEquals(null, $category->parent_category);
$category->load_parent_category();
$this->assertEquals($this->grade_categories[0]->id, $category->parent_category->id);
}
protected function sub_test_grade_category_get_parent_category() {
$category = new \grade_category($this->grade_categories[1]);
$this->assertTrue(method_exists($category, 'get_parent_category'));
$parent_category = $category->get_parent_category();
$this->assertEquals($this->grade_categories[0]->id, $parent_category->id);
}
/**
* Tests the getter of the category fullname with escaped HTML.
*/
protected function sub_test_grade_category_get_name_escaped() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'get_name'));
$this->assertEquals(format_string($this->grade_categories[0]->fullname, true, ['escape' => true]),
$category->get_name(true));
}
/**
* Tests the getter of the category fullname with unescaped HTML.
*/
protected function sub_test_grade_category_get_name_unescaped() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'get_name'));
$this->assertEquals(format_string($this->grade_categories[0]->fullname, true, ['escape' => false]),
$category->get_name(false));
}
protected function sub_test_grade_category_set_parent() {
$category = new \grade_category($this->grade_categories[1]);
$this->assertTrue(method_exists($category, 'set_parent'));
// TODO: implement detailed tests.
$course_category = \grade_category::fetch_course_category($this->courseid);
$this->assertTrue($category->set_parent($course_category->id));
$this->assertEquals($course_category->id, $category->parent);
}
protected function sub_test_grade_category_get_final() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'get_final'));
$category->load_grade_item();
$this->assertEquals($category->get_final(), $category->grade_item->get_final());
}
protected function sub_test_grade_category_get_sortorder() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'get_sortorder'));
$category->load_grade_item();
$this->assertEquals($category->get_sortorder(), $category->grade_item->get_sortorder());
}
protected function sub_test_grade_category_set_sortorder() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'set_sortorder'));
$category->load_grade_item();
$this->assertEquals($category->set_sortorder(10), $category->grade_item->set_sortorder(10));
}
protected function sub_test_grade_category_move_after_sortorder() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'move_after_sortorder'));
$category->load_grade_item();
$this->assertEquals($category->move_after_sortorder(10), $category->grade_item->move_after_sortorder(10));
}
protected function sub_test_grade_category_is_course_category() {
$category = \grade_category::fetch_course_category($this->courseid);
$this->assertTrue(method_exists($category, 'is_course_category'));
$this->assertTrue($category->is_course_category());
}
protected function sub_test_grade_category_fetch_course_category() {
$category = new \grade_category();
$this->assertTrue(method_exists($category, 'fetch_course_category'));
$category = \grade_category::fetch_course_category($this->courseid);
$this->assertTrue(empty($category->parent));
}
/**
* TODO implement
*/
protected function sub_test_grade_category_is_editable() {
}
protected function sub_test_grade_category_is_locked() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'is_locked'));
$category->load_grade_item();
$this->assertEquals($category->is_locked(), $category->grade_item->is_locked());
}
protected function sub_test_grade_category_set_locked() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'set_locked'));
// Even though a grade that needs updating cannot be locked, set_locked will return true because it will successfully
// schedule the locking for as soon as final grades are recalculated.
$this->assertTrue($category->set_locked(1));
// The category should not be locked yet as we are waiting for a recalculation.
$this->assertFalse($category->is_locked());
grade_regrade_final_grades($this->courseid);
// Get the category from the db again.
$category = new \grade_category($this->grade_categories[0]);
// The category is locked now.
$this->assertTrue($category->is_locked());
}
protected function sub_test_grade_category_is_hidden() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'is_hidden'));
$category->load_grade_item();
$this->assertEquals($category->is_hidden(), $category->grade_item->is_hidden());
}
protected function sub_test_grade_category_set_hidden() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue(method_exists($category, 'set_hidden'));
$category->set_hidden(1, true);
$category->load_grade_item();
$this->assertEquals(true, $category->grade_item->is_hidden());
}
protected function sub_test_grade_category_can_control_visibility() {
$category = new \grade_category($this->grade_categories[0]);
$this->assertTrue($category->can_control_visibility());
}
protected function sub_test_grade_category_insert_course_category() {
// Beware: adding a duplicate course category messes up the data in a way that's hard to recover from.
$grade_category = new \grade_category();
$this->assertTrue(method_exists($grade_category, 'insert_course_category'));
$id = $grade_category->insert_course_category($this->courseid);
$this->assertNotNull($id);
$this->assertEquals('?', $grade_category->fullname);
$this->assertEquals(GRADE_AGGREGATE_WEIGHTED_MEAN2, $grade_category->aggregation);
$this->assertEquals("/$id/", $grade_category->path);
$this->assertEquals(1, $grade_category->depth);
$this->assertNull($grade_category->parent);
}
protected function generate_random_raw_grade($item, $userid) {
$grade = new \grade_grade();
$grade->itemid = $item->id;
$grade->userid = $userid;
$grade->grademin = 0;
$grade->grademax = 1;
$valuetype = "grade$item->gradetype";
$grade->rawgrade = rand(0, 1000) / 1000;
$grade->insert();
return $grade->rawgrade;
}
protected function sub_test_grade_category_is_extracredit_used() {
$category = new \grade_category();
// Following use aggregationcoef.
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN2;
$this->assertTrue($category->is_extracredit_used());
$category->aggregation = GRADE_AGGREGATE_EXTRACREDIT_MEAN;
$this->assertTrue($category->is_extracredit_used());
$category->aggregation = GRADE_AGGREGATE_SUM;
$this->assertTrue($category->is_extracredit_used());
// Following don't use aggregationcoef.
$category->aggregation = GRADE_AGGREGATE_WEIGHTED_MEAN;
$this->assertFalse($category->is_extracredit_used());
$category->aggregation = GRADE_AGGREGATE_MAX;
$this->assertFalse($category->is_extracredit_used());
$category->aggregation = GRADE_AGGREGATE_MEAN;
$this->assertFalse($category->is_extracredit_used());
$category->aggregation = GRADE_AGGREGATE_MEDIAN;
$this->assertFalse($category->is_extracredit_used());
$category->aggregation = GRADE_AGGREGATE_MIN;
$this->assertFalse($category->is_extracredit_used());
$category->aggregation = GRADE_AGGREGATE_MODE;
$this->assertFalse($category->is_extracredit_used());
}
protected function sub_test_grade_category_aggregation_uses_extracredit() {
$this->assertTrue(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_WEIGHTED_MEAN2));
$this->assertTrue(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_EXTRACREDIT_MEAN));
$this->assertTrue(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_SUM));
$this->assertFalse(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_WEIGHTED_MEAN));
$this->assertFalse(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MAX));
$this->assertFalse(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MEAN));
$this->assertFalse(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MEDIAN));
$this->assertFalse(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MIN));
$this->assertFalse(\grade_category::aggregation_uses_extracredit(GRADE_AGGREGATE_MODE));
}
/**
* Test for category total visibility.
*/
protected function sub_test_grade_category_total_visibility() {
// 15 is a manual grade item in grade_categories[5].
$category = new \grade_category($this->grade_categories[5], true);
$gradeitem = new \grade_item($this->grade_items[15], true);
// Hide grade category.
$category->set_hidden(true, true);
$this->assertTrue($category->is_hidden());
// Category total is hidden.
$categorytotal = $category->get_grade_item();
$this->assertTrue($categorytotal->is_hidden());
// Manual grade is hidden.
$gradeitem->update_from_db();
$this->assertTrue($gradeitem->is_hidden());
// Unhide manual grade item.
$gradeitem->set_hidden(false);
$this->assertFalse($gradeitem->is_hidden());
// Category is unhidden.
$category->update_from_db();
$this->assertFalse($category->is_hidden());
// Category total remain hidden.
$categorytotal = $category->get_grade_item();
$this->assertTrue($categorytotal->is_hidden());
// Edit manual grade item.
$this->assertFalse($gradeitem->is_locked());
$gradeitem->set_locked(true);
$gradeitem->update_from_db();
$this->assertTrue($gradeitem->is_locked());
// Category total should still be hidden.
$category->update_from_db();
$categorytotal = $category->get_grade_item();
$this->assertTrue($categorytotal->is_hidden());
}
}
+741
View File
@@ -0,0 +1,741 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/fixtures/lib.php');
/**
* Test grade grades
*
* @package core
* @category test
* @copyright nicolas@moodle.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_grade_test extends \grade_base_testcase {
public function test_grade_grade(): void {
$this->sub_test_grade_grade_construct();
$this->sub_test_grade_grade_insert();
$this->sub_test_grade_grade_update();
$this->sub_test_grade_grade_fetch();
$this->sub_test_grade_grade_fetch_all();
$this->sub_test_grade_grade_load_grade_item();
$this->sub_test_grade_grade_standardise_score();
$this->sub_test_grade_grade_is_locked();
$this->sub_test_grade_grade_set_hidden();
$this->sub_test_grade_grade_is_hidden();
$this->sub_test_grade_grade_deleted();
$this->sub_test_grade_grade_deleted_event();
}
protected function sub_test_grade_grade_construct() {
$params = new \stdClass();
$params->itemid = $this->grade_items[0]->id;
$params->userid = 1;
$params->rawgrade = 88;
$params->rawgrademax = 110;
$params->rawgrademin = 18;
$grade_grade = new \grade_grade($params, false);
$this->assertEquals($params->itemid, $grade_grade->itemid);
$this->assertEquals($params->rawgrade, $grade_grade->rawgrade);
}
protected function sub_test_grade_grade_insert() {
$grade_grade = new \grade_grade();
$this->assertTrue(method_exists($grade_grade, 'insert'));
$grade_grade->itemid = $this->grade_items[0]->id;
$grade_grade->userid = 10;
$grade_grade->rawgrade = 88;
$grade_grade->rawgrademax = 110;
$grade_grade->rawgrademin = 18;
// Check the grade_item's needsupdate variable first.
$grade_grade->load_grade_item();
$this->assertEmpty($grade_grade->grade_item->needsupdate);
$grade_grade->insert();
$last_grade_grade = end($this->grade_grades);
$this->assertEquals($grade_grade->id, $last_grade_grade->id + 1);
// Timecreated will only be set if the grade was submitted by an activity module.
$this->assertTrue(empty($grade_grade->timecreated));
// Timemodified will only be set if the grade was submitted by an activity module.
$this->assertTrue(empty($grade_grade->timemodified));
// Keep our collection the same as is in the database.
$this->grade_grades[] = $grade_grade;
}
protected function sub_test_grade_grade_update() {
$grade_grade = new \grade_grade($this->grade_grades[0], false);
$this->assertTrue(method_exists($grade_grade, 'update'));
}
protected function sub_test_grade_grade_fetch() {
$grade_grade = new \grade_grade();
$this->assertTrue(method_exists($grade_grade, 'fetch'));
$grades = \grade_grade::fetch(array('id'=>$this->grade_grades[0]->id));
$this->assertEquals($this->grade_grades[0]->id, $grades->id);
$this->assertEquals($this->grade_grades[0]->rawgrade, $grades->rawgrade);
}
protected function sub_test_grade_grade_fetch_all() {
$grade_grade = new \grade_grade();
$this->assertTrue(method_exists($grade_grade, 'fetch_all'));
$grades = \grade_grade::fetch_all(array());
$this->assertEquals(count($this->grade_grades), count($grades));
}
protected function sub_test_grade_grade_load_grade_item() {
$grade_grade = new \grade_grade($this->grade_grades[0], false);
$this->assertTrue(method_exists($grade_grade, 'load_grade_item'));
$this->assertNull($grade_grade->grade_item);
$this->assertNotEmpty($grade_grade->itemid);
$this->assertNotNull($grade_grade->load_grade_item());
$this->assertNotNull($grade_grade->grade_item);
$this->assertEquals($this->grade_items[0]->id, $grade_grade->grade_item->id);
}
protected function sub_test_grade_grade_standardise_score() {
$this->assertEquals(4, round(\grade_grade::standardise_score(6, 0, 7, 0, 5)));
$this->assertEquals(40, \grade_grade::standardise_score(50, 30, 80, 0, 100));
}
/**
* Tests grade_grade::set_locked()
*
* @covers \grade_grade::set_locked
*/
public function test_grade_grade_set_locked(): void {
// Skip this test because set_locked() arguments have been modified, rendering these tests
// useless until they are re-written. Note this comes from MDL-32323 (2012!).
$this->markTestSkipped('Useless set_locked() tests until they are re-written');
$grade_item = new \grade_item($this->grade_items[0]);
$grade = new \grade_grade($grade_item->get_final(1));
$this->assertTrue(method_exists($grade, 'set_locked'));
$this->assertTrue(empty($grade_item->locked));
$this->assertTrue(empty($grade->locked));
$this->assertTrue($grade->set_locked(true));
$this->assertFalse(empty($grade->locked));
$this->assertTrue($grade->set_locked(false));
$this->assertTrue(empty($grade->locked));
$this->assertTrue($grade_item->set_locked(true, true));
$grade = new \grade_grade($grade_item->get_final(1));
$this->assertFalse(empty($grade->locked));
$this->assertFalse($grade->set_locked(true, false));
$this->assertTrue($grade_item->set_locked(true, false));
$grade = new \grade_grade($grade_item->get_final(1));
$this->assertTrue($grade->set_locked(true, false));
}
protected function sub_test_grade_grade_is_locked() {
$grade = new \grade_grade($this->grade_grades[0], false);
$this->assertTrue(method_exists($grade, 'is_locked'));
$this->assertFalse($grade->is_locked());
$grade->locked = time();
$this->assertTrue($grade->is_locked());
}
protected function sub_test_grade_grade_set_hidden() {
$grade = new \grade_grade($this->grade_grades[0], false);
$grade_item = new \grade_item($this->grade_items[0], false);
$this->assertTrue(method_exists($grade, 'set_hidden'));
$this->assertEquals(0, $grade_item->hidden);
$this->assertEquals(0, $grade->hidden);
$grade->set_hidden(0);
$this->assertEquals(0, $grade->hidden);
$grade->set_hidden(1);
$this->assertEquals(1, $grade->hidden);
$grade->set_hidden(0);
$this->assertEquals(0, $grade->hidden);
}
protected function sub_test_grade_grade_is_hidden() {
$grade = new \grade_grade($this->grade_grades[0], false);
$this->assertTrue(method_exists($grade, 'is_hidden'));
$this->assertFalse($grade->is_hidden());
$grade->hidden = 1;
$this->assertTrue($grade->is_hidden());
$grade->hidden = time()-666;
$this->assertFalse($grade->is_hidden());
$grade->hidden = time()+666;
$this->assertTrue($grade->is_hidden());
}
/**
* Test \grade_grade::flatten_dependencies_array()
*
* @covers \grade_grade::flatten_dependencies_array()
*/
public function test_flatten_dependencies(): void {
// First test a simple normal case.
$a = array(1 => array(2, 3), 2 => array(), 3 => array(4), 4 => array());
$b = array();
$expecteda = array(1 => array(2, 3, 4), 2 => array(), 3 => array(4), 4 => array());
$expectedb = array(1 => 1);
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
// Edge case - empty arrays.
$a = $b = $expecteda = $expectedb = array();
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
// Circular dependency.
$a = array(1 => array(2), 2 => array(3), 3 => array(1));
$b = array();
$expecteda = array(1 => array(1, 2, 3), 2 => array(1, 2, 3), 3 => array(1, 2, 3));
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
// Note - we don't test the depth when we got circular dependencies - the main thing we wanted to test was that there was
// no ka-boom. The result would be hard to understand and doesn't matter.
// Circular dependency 2.
$a = array(1 => array(2), 2 => array(3), 3 => array(4), 4 => array(2, 1));
$b = array();
$expecteda = array(1 => array(1, 2, 3, 4), 2 => array(1, 2, 3, 4), 3 => array(1, 2, 3, 4), 4 => array(1, 2, 3, 4));
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
// Missing first level dependency.
$a = array(1 => array(2, 3), 3 => array(4), 4 => array());
$b = array();
$expecteda = array(1 => array(2, 3, 4), 3 => array(4), 4 => array());
$expectedb = array(1 => 1);
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
// Missing 2nd level dependency.
$a = array(1 => array(2, 3), 2 => array(), 3 => array(4));
$b = array();
$expecteda = array(1 => array(2, 3, 4), 2 => array(), 3 => array(4));
$expectedb = array(1 => 1);
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
// Null first level dependency.
$a = array(1 => array(2, null), 2 => array(3), 3 => array(4), 4 => array());
$b = array();
$expecteda = array(1 => array(2, 3, 4), 2 => array(3, 4), 3 => array(4), 4 => array());
$expectedb = array(1 => 2, 2 => 1);
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
// Null 2nd level dependency.
$a = array(1 => array(2, 3), 2 => array(), 3 => array(4), 4 => array(null));
$b = array();
$expecteda = array(1 => array(2, 3, 4), 2 => array(), 3 => array(4), 4 => array());
$expectedb = array(1 => 1);
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
// Straight null dependency.
$a = array(1 => array(2, 3), 2 => array(), 3 => array(4), 4 => null);
$b = array();
$expecteda = array(1 => array(2, 3, 4), 2 => array(), 3 => array(4), 4 => array());
$expectedb = array(1 => 1);
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
// Also incorrect non-array dependency.
$a = array(1 => array(2, 3), 2 => array(), 3 => array(4), 4 => 23);
$b = array();
$expecteda = array(1 => array(2, 3, 4), 2 => array(), 3 => array(4), 4 => array());
$expectedb = array(1 => 1);
\test_grade_grade_flatten_dependencies_array::test_flatten_dependencies_array($a, $b);
$this->assertSame($expecteda, $a);
$this->assertSame($expectedb, $b);
}
public function test_grade_grade_min_max(): void {
global $CFG;
$initialminmaxtouse = $CFG->grade_minmaxtouse;
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$assignrecord = $this->getDataGenerator()->create_module('assign', array('course' => $course, 'grade' => 100));
$cm = get_coursemodule_from_instance('assign', $assignrecord->id);
$assigncontext = \context_module::instance($cm->id);
$assign = new \assign($assigncontext, $cm, $course);
// Fetch the assignment item.
$giparams = array('itemtype' => 'mod', 'itemmodule' => 'assign', 'iteminstance' => $assignrecord->id,
'courseid' => $course->id, 'itemnumber' => 0);
$gi = \grade_item::fetch($giparams);
$this->assertEquals(0, $gi->grademin);
$this->assertEquals(100, $gi->grademax);
// Give a grade to the student.
$usergrade = $assign->get_user_grade($user->id, true);
$usergrade->grade = 10;
$assign->update_grade($usergrade);
// Check the grade stored in gradebook.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(10, $gg->rawgrade);
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Change the min/max grade of the item.
$gi->grademax = 50;
$gi->grademin = 2;
$gi->update();
// Fetch the updated item.
$gi = \grade_item::fetch($giparams);
// Now check the grade grade min/max with system setting.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(2, $gg->get_grade_min());
$this->assertEquals(50, $gg->get_grade_max());
// Now with other system setting.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting, and reset static cache.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Now with overriden setting in course.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_GRADE);
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_ITEM);
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(2, $gg->get_grade_min());
$this->assertEquals(50, $gg->get_grade_max());
$CFG->grade_minmaxtouse = $initialminmaxtouse;
}
public function test_grade_grade_min_max_with_course_item(): void {
global $CFG, $DB;
$initialminmaxtouse = $CFG->grade_minmaxtouse;
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$gi = \grade_item::fetch_course_item($course->id);
// Fetch the category item.
$this->assertEquals(0, $gi->grademin);
$this->assertEquals(100, $gi->grademax);
// Give a grade to the student.
$gi->update_final_grade($user->id, 10);
// Check the grade min/max stored in gradebook.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Change the min/max grade of the item.
$gi->grademin = 2;
$gi->grademax = 50;
$gi->update();
// Fetch the updated item.
$gi = \grade_item::fetch_course_item($course->id);
// Now check the grade grade min/max with system setting.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Now with other system setting.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting, and reset static cache.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Now with overriden setting in course.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_GRADE);
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_ITEM);
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
$CFG->grade_minmaxtouse = $initialminmaxtouse;
}
public function test_grade_grade_min_max_with_category_item(): void {
global $CFG, $DB;
$initialminmaxtouse = $CFG->grade_minmaxtouse;
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$coursegi = \grade_item::fetch_course_item($course->id);
// Create a category item.
$gc = new \grade_category(array('courseid' => $course->id, 'fullname' => 'test'), false);
$gc->insert();
$gi = $gc->get_grade_item();
$gi->grademax = 100;
$gi->grademin = 0;
$gi->update();
// Fetch the category item.
$giparams = array('itemtype' => 'category', 'iteminstance' => $gc->id);
$gi = \grade_item::fetch($giparams);
$this->assertEquals(0, $gi->grademin);
$this->assertEquals(100, $gi->grademax);
// Give a grade to the student.
$gi->update_final_grade($user->id, 10);
// Check the grade min/max stored in gradebook.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Change the min/max grade of the item.
$gi->grademin = 2;
$gi->grademax = 50;
$gi->update();
// Fetch the updated item.
$gi = \grade_item::fetch($giparams);
// Now check the grade grade min/max with system setting.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Now with other system setting.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
grade_set_setting($course->id, 'minmaxtouse', null); // Ensure no course setting, and reset static cache.
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
// Now with overriden setting in course.
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_ITEM;
grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_GRADE);
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
$CFG->grade_minmaxtouse = GRADE_MIN_MAX_FROM_GRADE_GRADE;
grade_set_setting($course->id, 'minmaxtouse', GRADE_MIN_MAX_FROM_GRADE_ITEM);
$gg = \grade_grade::fetch(array('userid' => $user->id, 'itemid' => $gi->id));
$this->assertEquals(0, $gg->get_grade_min());
$this->assertEquals(100, $gg->get_grade_max());
$CFG->grade_minmaxtouse = $initialminmaxtouse;
}
/**
* Tests when a grade_grade has been deleted.
*/
public function sub_test_grade_grade_deleted() {
$dg = $this->getDataGenerator();
// Create the data we need for the tests.
$fs = new \file_storage();
$u1 = $dg->create_user();
$c1 = $dg->create_course();
$a1 = $dg->create_module('assign', ['course' => $c1->id]);
$a1context = \context_module::instance($a1->cmid);
$gi = new \grade_item($dg->create_grade_item(
[
'courseid' => $c1->id,
'itemtype' => 'mod',
'itemmodule' => 'assign',
'iteminstance' => $a1->id
]
), false);
// Add feedback files to copy as our update.
$this->add_feedback_file_to_copy();
$grades['feedback'] = 'Nice feedback!';
$grades['feedbackformat'] = FORMAT_MOODLE;
$grades['feedbackfiles'] = [
'contextid' => 1,
'component' => 'test',
'filearea' => 'testarea',
'itemid' => 1
];
$grades['userid'] = $u1->id;
grade_update('mod/assign', $gi->courseid, $gi->itemtype, $gi->itemmodule, $gi->iteminstance,
$gi->itemnumber, $grades);
// Feedback file area.
$files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
$this->assertEquals(2, count($files));
// History file area.
$files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
$this->assertEquals(2, count($files));
$gg = \grade_grade::fetch(array('userid' => $u1->id, 'itemid' => $gi->id));
$gg->delete();
// Feedback file area.
$files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_FEEDBACK_FILEAREA);
$this->assertEquals(0, count($files));
// History file area.
$files = $fs->get_area_files($a1context->id, GRADE_FILE_COMPONENT, GRADE_HISTORY_FEEDBACK_FILEAREA);
$this->assertEquals(2, count($files));
}
/**
* Creates a feedback file to copy to the gradebook area.
*/
private function add_feedback_file_to_copy() {
$dummy = array(
'contextid' => 1,
'component' => 'test',
'filearea' => 'testarea',
'itemid' => 1,
'filepath' => '/',
'filename' => 'feedback1.txt'
);
$fs = get_file_storage();
$fs->create_file_from_string($dummy, '');
}
/**
* Tests grade_deleted event.
*/
public function sub_test_grade_grade_deleted_event() {
global $DB;
$dg = $this->getDataGenerator();
// Create the data we need for the tests.
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$c1 = $dg->create_course();
$a1 = $dg->create_module('assign', ['course' => $c1->id]);
$gi = new \grade_item($dg->create_grade_item(
[
'courseid' => $c1->id,
'itemtype' => 'mod',
'itemmodule' => 'assign',
'iteminstance' => $a1->id
]
), false);
grade_update('mod/assign', $gi->courseid, $gi->itemtype, $gi->itemmodule, $gi->iteminstance,
$gi->itemnumber, ['userid' => $u1->id]);
grade_update('mod/assign', $gi->courseid, $gi->itemtype, $gi->itemmodule, $gi->iteminstance,
$gi->itemnumber, ['userid' => $u2->id]);
$gg = \grade_grade::fetch(array('userid' => $u1->id, 'itemid' => $gi->id));
$this->assertEquals($u1->id, $gg->userid);
$gg->load_grade_item();
$this->assertEquals($gi->id, $gg->grade_item->id);
// Delete user with valid grade item.
$sink = $this->redirectEvents();
grade_user_delete($u1->id);
$events = $sink->get_events();
$event = reset($events);
$sink->close();
$this->assertInstanceOf('core\event\grade_deleted', $event);
$gg = \grade_grade::fetch(array('userid' => $u2->id, 'itemid' => $gi->id));
$this->assertEquals($u2->id, $gg->userid);
$gg->load_grade_item();
$this->assertEquals($gi->id, $gg->grade_item->id);
// Delete grade item, mock up orphaned grade_grades.
$DB->delete_records('grade_items', ['id' => $gi->id]);
$gg = \grade_grade::fetch(array('userid' => $u2->id, 'itemid' => $gi->id));
$this->assertEquals($u2->id, $gg->userid);
// No event is triggered and there is a debugging message.
$sink = $this->redirectEvents();
grade_user_delete($u2->id);
$this->assertDebuggingCalled("Missing grade item id $gi->id");
$events = $sink->get_events();
$sink->close();
$this->assertEmpty($events);
// The grade should be deleted.
$gg = \grade_grade::fetch(array('userid' => $u2->id, 'itemid' => $gi->id));
$this->assertEmpty($gg);
}
/**
* Tests get_hiding_affected by locked category and overridden grades.
*/
public function test_category_get_hiding_affected(): void {
$generator = $this->getDataGenerator();
// Create the data we need for the tests.
$course1 = $generator->create_course();
$user1 = $generator->create_and_enrol($course1, 'student');
$assignment2 = $generator->create_module('assign', ['course' => $course1->id]);
// Create a category item.
$gradecategory = new \grade_category(array('courseid' => $course1->id, 'fullname' => 'test'), false);
$gradecategoryid = $gradecategory->insert();
// Create one hidden grade item.
$gradeitem1a = new \grade_item($generator->create_grade_item(
[
'courseid' => $course1->id,
'itemtype' => 'mod',
'itemmodule' => 'assign',
'iteminstance' => $assignment2->id,
'categoryid' => $gradecategoryid,
'hidden' => 1,
]
), false);
grade_update('mod/assign', $gradeitem1a->courseid, $gradeitem1a->itemtype, $gradeitem1a->itemmodule, $gradeitem1a->iteminstance,
$gradeitem1a->itemnumber, ['userid' => $user1->id]);
// Get category grade item.
$gradeitem = $gradecategory->get_grade_item();
// Reset needsupdate to allow set_locked.
$gradeitem->needsupdate = 0;
$gradeitem->update();
// Lock category grade item.
$gradeitem->set_locked(1);
$hidingaffectedlocked = $this->call_get_hiding_affected($course1, $user1);
// Since locked category now should be recalculated.
// The number of unknown items is 2, this includes category item and course item.
$this->assertEquals(2, count($hidingaffectedlocked['unknown']));
// Unlock category.
$gradeitem->set_locked(0);
$hidingaffectedunlocked = $this->call_get_hiding_affected($course1, $user1);
// When category unlocked, hidden item should exist in altered items.
$this->assertTrue(in_array($gradeitem1a->id, array_keys($hidingaffectedunlocked['altered'])));
// This creates all the grade_grades we need.
grade_regrade_final_grades($course1->id);
// Set grade override.
$gradegrade = \grade_grade::fetch([
'userid' => $user1->id,
'itemid' => $gradeitem->id,
]);
// Set override grade grade, and check that grade submission has been overridden.
$gradegrade->set_overridden(true);
$this->assertEquals(true, $gradegrade->is_overridden());
$hidingaffectedoverridden = $this->call_get_hiding_affected($course1, $user1);
// No need to recalculate overridden grades.
$this->assertTrue(in_array($gradegrade->itemid, array_keys($hidingaffectedoverridden['alteredaggregationstatus'])));
$this->assertEquals('used', $hidingaffectedoverridden['alteredaggregationstatus'][$gradegrade->itemid]);
}
/**
* Call get_hiding_affected().
* @param \stdClass $course The course object
* @param \stdClass $user The student object
* @return array
*/
private function call_get_hiding_affected($course, $user) {
global $DB;
$items = \grade_item::fetch_all(array('courseid' => $course->id));
$grades = array();
$sql = "SELECT g.*
FROM {grade_grades} g
JOIN {grade_items} gi ON gi.id = g.itemid
WHERE g.userid = :userid AND gi.courseid = :courseid";
if ($gradesrecords = $DB->get_records_sql($sql, ['userid' => $user->id, 'courseid' => $course->id])) {
foreach ($gradesrecords as $grade) {
$grades[$grade->itemid] = new \grade_grade($grade, false);
}
unset($gradesrecords);
}
foreach ($items as $itemid => $gradeitem) {
if (!isset($grades[$itemid])) {
$gradegrade = new \grade_grade();
$gradegrade->userid = $user->id;
$gradegrade->itemid = $gradeitem->id;
$grades[$itemid] = $gradegrade;
}
}
return \grade_grade::get_hiding_affected($grades, $items);
}
}
File diff suppressed because it is too large Load Diff
+70
View File
@@ -0,0 +1,70 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/fixtures/lib.php');
/**
* Grade object testcase.
*
* @package core
* @category test
* @copyright 2014 Frédéric Massart - FMCorz.net
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_object_test extends \grade_base_testcase {
public function test_fetch_all_helper(): void {
// Simple ID lookup.
$params = array('id' => $this->grade_items[0]->id);
$items = \grade_object::fetch_all_helper('grade_items', 'grade_item', $params);
$this->assertCount(1, $items);
$item = array_shift($items);
$this->assertInstanceOf('grade_item', $item);
$this->assertEquals($item->id, $this->grade_items[0]->id);
// Various parameters lookup, multiple results.
$params = array('courseid' => $this->course->id, 'categoryid' => $this->grade_categories[1]->id);
$items = \grade_object::fetch_all_helper('grade_items', 'grade_item', $params);
$this->assertCount(2, $items);
$expecteditems = array($this->grade_items[0]->id => true, $this->grade_items[1]->id => true);
foreach ($items as $item) {
$this->assertInstanceOf('grade_item', $item);
$this->assertArrayHasKey($item->id, $expecteditems);
unset($expecteditems[$item->id]);
}
// Text column lookup.
$params = array('iteminfo' => $this->grade_items[2]->iteminfo);
$items = \grade_object::fetch_all_helper('grade_items', 'grade_item', $params);
$this->assertCount(1, $items);
$item = array_shift($items);
$this->assertInstanceOf('grade_item', $item);
$this->assertEquals($item->id, $this->grade_items[2]->id);
// Lookup using non-existing columns.
$params = array('doesnotexist' => 'ignoreme', 'id' => $this->grade_items[0]->id);
$items = \grade_object::fetch_all_helper('grade_items', 'grade_item', $params);
$this->assertCount(1, $items);
$item = array_shift($items);
$this->assertInstanceOf('grade_item', $item);
$this->assertEquals($item->id, $this->grade_items[0]->id);
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/fixtures/lib.php');
/**
* Unit tests for grade_outcome
*
* @package core
* @category test
* @copyright nicolas@moodle.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_outcome_test extends \grade_base_testcase {
public function test_grade_outcome(): void {
$this->sub_test_grade_outcome_construct();
$this->sub_test_grade_outcome_insert();
$this->sub_test_grade_outcome_update();
$this->sub_test_grade_outcome_delete();
//$this->sub_test_grade_outcome_fetch();
$this->sub_test_grade_outcome_fetch_all();
}
protected function sub_test_grade_outcome_construct() {
$params = new \stdClass();
$params->courseid = $this->courseid;
$params->shortname = 'Team work';
$grade_outcome = new \grade_outcome($params, false);
$this->assertEquals($params->courseid, $grade_outcome->courseid);
$this->assertEquals($params->shortname, $grade_outcome->shortname);
}
protected function sub_test_grade_outcome_insert() {
$grade_outcome = new \grade_outcome();
$this->assertTrue(method_exists($grade_outcome, 'insert'));
$grade_outcome->courseid = $this->courseid;
$grade_outcome->shortname = 'tw';
$grade_outcome->fullname = 'Team work';
$grade_outcome->insert();
$last_grade_outcome = end($this->grade_outcomes);
$this->assertEquals($grade_outcome->id, $last_grade_outcome->id + 1);
$this->assertFalse(empty($grade_outcome->timecreated));
$this->assertFalse(empty($grade_outcome->timemodified));
}
protected function sub_test_grade_outcome_update() {
global $DB;
$grade_outcome = new \grade_outcome($this->grade_outcomes[0], false);
$this->assertTrue(method_exists($grade_outcome, 'update'));
$grade_outcome->shortname = 'Team work';
$this->assertTrue($grade_outcome->update());
$shortname = $DB->get_field('grade_outcomes', 'shortname', array('id' => $this->grade_outcomes[0]->id));
$this->assertEquals($grade_outcome->shortname, $shortname);
}
protected function sub_test_grade_outcome_delete() {
global $DB;
$grade_outcome = new \grade_outcome($this->grade_outcomes[0], false);
$this->assertTrue(method_exists($grade_outcome, 'delete'));
$this->assertTrue($grade_outcome->delete());
$this->assertFalse($DB->get_record('grade_outcomes', array('id' => $grade_outcome->id)));
}
protected function sub_test_grade_outcome_fetch() {
$grade_outcome = new \grade_outcome();
$this->assertTrue(method_exists($grade_outcome, 'fetch'));
$grade_outcome = \grade_outcome::fetch(array('id'=>$this->grade_outcomes[0]->id));
$grade_outcome->load_scale();
$this->assertEquals($this->grade_outcomes[0]->id, $grade_outcome->id);
$this->assertEquals($this->grade_outcomes[0]->shortname, $grade_outcome->shortname);
$this->assertEquals($this->scale[2]->id, $grade_outcome->scale->id);
}
protected function sub_test_grade_outcome_fetch_all() {
$grade_outcome = new \grade_outcome();
$this->assertTrue(method_exists($grade_outcome, 'fetch_all'));
$grade_outcomes = \grade_outcome::fetch_all(array());
$this->assertEquals(count($this->grade_outcomes), count($grade_outcomes));
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle 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 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/fixtures/lib.php');
/**
* Unit tests for grade_scale
*
* @package core
* @category test
* @copyright nicolas@moodle.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_scale_test extends \grade_base_testcase {
public function test_grade_scale(): void {
$this->sub_test_scale_construct();
$this->sub_test_grade_scale_insert();
$this->sub_test_grade_scale_update();
$this->sub_test_grade_scale_delete();
$this->sub_test_grade_scale_fetch();
$this->sub_test_scale_load_items();
$this->sub_test_scale_compact_items();
$this->sub_test_scale_one_item();
}
protected function sub_test_scale_construct() {
$params = new \stdClass();
$params->name = 'unittestscale3';
$params->courseid = $this->course->id;
$params->userid = $this->userid;
$params->scale = 'Distinction, Very Good, Good, Pass, Fail';
$params->description = 'This scale is used to mark standard assignments.';
$params->timemodified = time();
$scale = new \grade_scale($params, false);
$this->assertEquals($params->name, $scale->name);
$this->assertEquals($params->scale, $scale->scale);
$this->assertEquals($params->description, $scale->description);
}
protected function sub_test_grade_scale_insert() {
$grade_scale = new \grade_scale();
$this->assertTrue(method_exists($grade_scale, 'insert'));
$grade_scale->name = 'unittestscale3';
$grade_scale->courseid = $this->courseid;
$grade_scale->userid = $this->userid;
$grade_scale->scale = 'Distinction, Very Good, Good, Pass, Fail';
$grade_scale->description = 'This scale is used to mark standard assignments.';
$grade_scale->insert();
$last_grade_scale = end($this->scale);
$this->assertEquals($grade_scale->id, $last_grade_scale->id + 1);
$this->assertNotEmpty($grade_scale->timecreated);
$this->assertNotEmpty($grade_scale->timemodified);
}
protected function sub_test_grade_scale_update() {
global $DB;
$grade_scale = new \grade_scale($this->scale[1], false);
$this->assertTrue(method_exists($grade_scale, 'update'));
$grade_scale->name = 'Updated info for this unittest grade_scale';
$this->assertTrue($grade_scale->update());
$name = $DB->get_field('scale', 'name', array('id' => $this->scale[1]->id));
$this->assertEquals($grade_scale->name, $name);
}
protected function sub_test_grade_scale_delete() {
global $DB;
$grade_scale = new \grade_scale($this->scale[4], false); // Choose one we're not using elsewhere.
$this->assertTrue(method_exists($grade_scale, 'delete'));
$this->assertTrue($grade_scale->delete());
$this->assertFalse($DB->get_record('scale', array('id' => $grade_scale->id)));
// Keep the reference collection the same as what is in the database.
unset($this->scale[4]);
}
protected function sub_test_grade_scale_fetch() {
$grade_scale = new \grade_scale();
$this->assertTrue(method_exists($grade_scale, 'fetch'));
$grade_scale = \grade_scale::fetch(array('id'=>$this->scale[0]->id));
$this->assertEquals($this->scale[0]->id, $grade_scale->id);
$this->assertEquals($this->scale[0]->name, $grade_scale->name);
}
protected function sub_test_scale_load_items() {
$scale = new \grade_scale($this->scale[0], false);
$this->assertTrue(method_exists($scale, 'load_items'));
$scale->load_items();
$this->assertCount(7, $scale->scale_items);
$this->assertEquals('Fairly neutral', $scale->scale_items[2]);
}
protected function sub_test_scale_compact_items() {
$scale = new \grade_scale($this->scale[0], false);
$this->assertTrue(method_exists($scale, 'compact_items'));
$scale->load_items();
$scale->scale = null;
$scale->compact_items();
// The original string and the new string may have differences in whitespace around the delimiter, and that's OK.
$this->assertEquals(preg_replace('/\s*,\s*/', ',', $this->scale[0]->scale), $scale->scale);
}
protected function sub_test_scale_one_item() {
$params = new \stdClass();
$params->name = 'unittestscale1i';
$params->courseid = $this->course->id;
$params->userid = $this->userid;
$params->scale = 'Like';
$params->description = 'This scale is used to like something.';
$params->timemodified = time();
$scale = new \grade_scale($params, false);
$scale->load_items();
$this->assertCount(1, $scale->scale_items);
$this->assertSame(array('Like'), $scale->scale_items);
$this->assertSame('Like', $scale->compact_items());
$scale->insert();
// Manual grade item with 1 item scale.
$grade_item = new \stdClass();
$grade_item->courseid = $this->course->id;
$grade_item->categoryid = $this->grade_categories[0]->id;
$grade_item->itemname = 'manual grade_item scale_1';
$grade_item->itemtype = 'manual';
$grade_item->itemnumber = 0;
$grade_item->needsupdate = false;
$grade_item->gradetype = GRADE_TYPE_SCALE;
$grade_item->scaleid = $scale->id;
$grade_item->iteminfo = 'Manual grade item used for unit testing';
$grade_item->timecreated = time();
$grade_item->timemodified = time();
$grade_item = new \grade_item($grade_item);
$grade_item->insert();
$this->assertNotEmpty($grade_item->id);
$this->assertEquals(1, $grade_item->grademin);
$this->assertEquals(1, $grade_item->grademax);
$status = $scale->is_used();
$this->assertTrue($status);
}
}