mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 00:12:56 +02:00
Grading grades aggregations
This commit is contained in:
parent
2a9b468c35
commit
39411930fa
@ -1189,7 +1189,8 @@ class workshop {
|
||||
/**
|
||||
* Calculates grades for assessment for the given participant(s)
|
||||
*
|
||||
* Grade for submission is calculated as a weighted mean of all given grades
|
||||
* Grade for assessment is calculated as a simple mean of all grading grades calculated by the grading evaluator.
|
||||
* The assessment weight is not taken into account here.
|
||||
*
|
||||
* @param null|int|array $restrict If null, update all reviewers, otherwise update just grades for the given reviewer(s)
|
||||
* @return void
|
||||
@ -1197,7 +1198,50 @@ class workshop {
|
||||
public function aggregate_grading_grades($restrict=null) {
|
||||
global $DB;
|
||||
|
||||
// todo
|
||||
// fetch a recordset with all assessments to process
|
||||
$sql = 'SELECT a.reviewerid, a.gradinggrade, a.gradinggradeover,
|
||||
ag.id AS aggregationid, ag.gradinggrade AS aggregatedgrade
|
||||
FROM {workshop_assessments} a
|
||||
INNER JOIN {workshop_submissions} s ON (a.submissionid = s.id)
|
||||
LEFT JOIN {workshop_aggregations} ag ON (ag.userid = a.reviewerid AND ag.workshopid = s.workshopid)
|
||||
WHERE s.example=0 AND s.workshopid=:workshopid'; // to be cont.
|
||||
$params = array('workshopid' => $this->id);
|
||||
|
||||
if (is_null($restrict)) {
|
||||
// update all users - no more conditions
|
||||
} elseif (!empty($restrict)) {
|
||||
list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
|
||||
$sql .= " AND a.reviewerid $usql";
|
||||
$params = array_merge($params, $uparams);
|
||||
} else {
|
||||
throw new coding_exception('Empty value is not a valid parameter here');
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY a.reviewerid'; // this is important for bulk processing
|
||||
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
$batch = array(); // will contain a set of all assessments of a single submission
|
||||
$previous = null; // a previous record in the recordset
|
||||
|
||||
foreach ($rs as $current) {
|
||||
if (is_null($previous)) {
|
||||
// we are processing the very first record in the recordset
|
||||
$previous = $current;
|
||||
}
|
||||
if ($current->reviewerid == $previous->reviewerid) {
|
||||
// we are still processing the current reviewer
|
||||
$batch[] = $current;
|
||||
} else {
|
||||
// process all the assessments of a sigle submission
|
||||
$this->aggregate_grading_grades_process($batch);
|
||||
// and then start to process another reviewer
|
||||
$batch = array($current);
|
||||
$previous = $current;
|
||||
}
|
||||
}
|
||||
// do not forget to process the last batch!
|
||||
$this->aggregate_grading_grades_process($batch);
|
||||
$rs->close();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1295,6 +1339,68 @@ class workshop {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of all assessments done by a single reviewer, calculates the final grading grade
|
||||
*
|
||||
* This calculates the simple mean of the passed grading grades. If, however, the grading grade
|
||||
* was overridden by a teacher, the gradinggradeover value is returned and the rest of grades are ignored.
|
||||
*
|
||||
* @param array $assessments of stdClass(->reviewerid ->gradinggrade ->gradinggradeover ->aggregationid ->aggregatedgrade)
|
||||
* @return null|float the aggregated grade rounded to numeric(10,5)
|
||||
*/
|
||||
protected function aggregate_grading_grades_process(array $assessments) {
|
||||
global $DB;
|
||||
|
||||
$reviewerid = null; // the id of the reviewer being processed
|
||||
$current = null; // the gradinggrade currently saved in database
|
||||
$finalgrade = null; // the new grade to be calculated
|
||||
$agid = null; // aggregation id
|
||||
$sumgrades = 0;
|
||||
$count = 0;
|
||||
|
||||
foreach ($assessments as $assessment) {
|
||||
if (is_null($reviewerid)) {
|
||||
// the id is the same in all records, fetch it during the first loop cycle
|
||||
$reviewerid = $assessment->reviewerid;
|
||||
}
|
||||
if (is_null($agid)) {
|
||||
// the id is the same in all records, fetch it during the first loop cycle
|
||||
$agid = $assessment->aggregationid;
|
||||
}
|
||||
if (is_null($current)) {
|
||||
// the currently saved grade is the same in all records, fetch it during the first loop cycle
|
||||
$current = $assessment->aggregatedgrade;
|
||||
}
|
||||
if (!is_null($assessment->gradinggradeover)) {
|
||||
// the grading grade for this assessment is overriden by a teacher
|
||||
$sumgrades += $assessment->gradinggradeover;
|
||||
$count++;
|
||||
} else {
|
||||
if (!is_null($assessment->gradinggrade)) {
|
||||
$sumgrades += $assessment->gradinggrade;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($count > 0) {
|
||||
$finalgrade = grade_floatval($sumgrades / $count);
|
||||
}
|
||||
// check if the new final grade differs from the one stored in the database
|
||||
if (grade_floats_different($finalgrade, $current)) {
|
||||
// we need to save new calculation into the database
|
||||
if (is_null($agid)) {
|
||||
// no aggregation record yet
|
||||
$record = new stdClass();
|
||||
$record->workshopid = $this->id;
|
||||
$record->userid = $reviewerid;
|
||||
$record->gradinggrade = $finalgrade;
|
||||
$DB->insert_record('workshop_aggregations', $record);
|
||||
} else {
|
||||
$DB->set_field('workshop_aggregations', 'gradinggrade', $finalgrade, array('id' => $agid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of user ids, returns the filtered one containing just ids of users with own submission
|
||||
*
|
||||
|
@ -36,6 +36,7 @@ Mock::generate(get_class($DB), 'mockDB');
|
||||
class testable_workshop extends workshop {
|
||||
|
||||
public function __construct() {
|
||||
$this->id = 16;
|
||||
$this->cm = new stdClass();
|
||||
$this->course = new stdClass();
|
||||
$this->context = new stdClass();
|
||||
@ -44,6 +45,10 @@ class testable_workshop extends workshop {
|
||||
public function aggregate_submission_grades_process(array $assessments) {
|
||||
parent::aggregate_submission_grades_process($assessments);
|
||||
}
|
||||
|
||||
public function aggregate_grading_grades_process(array $assessments) {
|
||||
parent::aggregate_grading_grades_process($assessments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -243,6 +248,135 @@ class workshop_internal_api_test extends UnitTestCase {
|
||||
$this->workshop->aggregate_submission_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_nograding() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>2, 'gradinggrade'=>null, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
|
||||
// expectation
|
||||
$DB->expectNever('set_field');
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_single_grade_new() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>82.87670, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
|
||||
// expectation
|
||||
$expected = new stdClass();
|
||||
$expected->workshopid = $this->workshop->id;
|
||||
$expected->userid = 3;
|
||||
$expected->gradinggrade = 82.87670;
|
||||
$DB->expectOnce('insert_record', array('workshop_aggregations', $expected));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_single_grade_update() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>90.00000, 'gradinggradeover'=>null, 'aggregationid'=>1, 'aggregatedgrade'=>82.87670);
|
||||
// expectation
|
||||
$DB->expectOnce('set_field', array('workshop_aggregations', 'gradinggrade', 90.00000, array('id' => 1)));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_single_grade_uptodate() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>3, 'gradinggrade'=>90.00000, 'gradinggradeover'=>null, 'aggregationid'=>1, 'aggregatedgrade'=>90.00000);
|
||||
// expectation
|
||||
$DB->expectNever('set_field');
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_single_grade_overridden() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>4, 'gradinggrade'=>91.56700, 'gradinggradeover'=>82.32105, 'aggregationid'=>2, 'aggregatedgrade'=>91.56700);
|
||||
// expectation
|
||||
$DB->expectOnce('set_field', array('workshop_aggregations', 'gradinggrade', 82.32105, array('id' => 2)));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_multiple_grades_new() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>99.45670, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>null, 'aggregatedgrade'=>null);
|
||||
// expectation
|
||||
$expected = new stdClass();
|
||||
$expected->workshopid = $this->workshop->id;
|
||||
$expected->userid = 5;
|
||||
$expected->gradinggrade = 79.3066;
|
||||
$DB->expectOnce('insert_record', array('workshop_aggregations', $expected));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_multiple_grades_update() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>56.23400, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660);
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660);
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>79.30660);
|
||||
// expectation
|
||||
$DB->expectOnce('set_field', array('workshop_aggregations', 'gradinggrade', 64.89904, array('id' => 2)));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_multiple_grades_overriden() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>56.23400, 'gradinggradeover'=>99.45670, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904);
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>87.34311, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904);
|
||||
$batch[] = (object)array('reviewerid'=>5, 'gradinggrade'=>51.12000, 'gradinggradeover'=>null, 'aggregationid'=>2, 'aggregatedgrade'=>64.89904);
|
||||
// expectation
|
||||
$DB->expectOnce('set_field', array('workshop_aggregations', 'gradinggrade', 79.30660, array('id' => 2)));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_multiple_grades_one_missing() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>50.00000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
|
||||
$batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>null, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
|
||||
$batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>52.20000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
|
||||
// expectation
|
||||
$DB->expectOnce('set_field', array('workshop_aggregations', 'gradinggrade', 51.10000, array('id' => 3)));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
public function test_aggregate_grading_grades_process_multiple_grades_missing_overridden() {
|
||||
global $DB;
|
||||
// fixture set-up
|
||||
$batch = array();
|
||||
$batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>50.00000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
|
||||
$batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>null, 'gradinggradeover'=>69.00000, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
|
||||
$batch[] = (object)array('reviewerid'=>6, 'gradinggrade'=>52.20000, 'gradinggradeover'=>null, 'aggregationid'=>3, 'aggregatedgrade'=>100.00000);
|
||||
// expectation
|
||||
$DB->expectOnce('set_field', array('workshop_aggregations', 'gradinggrade', 57.06667, array('id' => 3)));
|
||||
// excersise SUT
|
||||
$this->workshop->aggregate_grading_grades_process($batch);
|
||||
}
|
||||
|
||||
|
||||
public function test_percent_to_value() {
|
||||
// fixture setup
|
||||
$total = 185;
|
||||
|
Loading…
x
Reference in New Issue
Block a user