From 39411930faab65049c3470efada7182086872a27 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Mon, 4 Jan 2010 18:17:37 +0000 Subject: [PATCH] Grading grades aggregations --- mod/workshop/locallib.php | 110 ++++++++++++++++++- mod/workshop/simpletest/testlocallib.php | 134 +++++++++++++++++++++++ 2 files changed, 242 insertions(+), 2 deletions(-) diff --git a/mod/workshop/locallib.php b/mod/workshop/locallib.php index f076c4651a..9c24cb699a 100644 --- a/mod/workshop/locallib.php +++ b/mod/workshop/locallib.php @@ -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 * diff --git a/mod/workshop/simpletest/testlocallib.php b/mod/workshop/simpletest/testlocallib.php index c6f2b8e20f..4e9839450a 100644 --- a/mod/workshop/simpletest/testlocallib.php +++ b/mod/workshop/simpletest/testlocallib.php @@ -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; -- 2.39.5