]> git.mjollnir.org Git - moodle.git/commitdiff
Workshop total grade calculation support
authorDavid Mudrak <david.mudrak@gmail.com>
Mon, 4 Jan 2010 18:18:00 +0000 (18:18 +0000)
committerDavid Mudrak <david.mudrak@gmail.com>
Mon, 4 Jan 2010 18:18:00 +0000 (18:18 +0000)
mod/workshop/lang/en_utf8/workshop.php
mod/workshop/locallib.php
mod/workshop/renderer.php
mod/workshop/simpletest/testlocallib.php
mod/workshop/styles.php
mod/workshop/view.php

index 62b36af6a6fd5a89166f1c406df357e01c7fffd7..a6c97a27f1f983741ff629562ae893cdd4e41df7 100644 (file)
@@ -28,9 +28,6 @@ defined('MOODLE_INTERNAL') || die();
 $string[''] = '';
 $string[''] = '';
 $string[''] = '';
-$string[''] = '';
-$string['configgradedecimals'] = 'Default number of digits that should be shown after the decimal point when displaying grades.';
-$string['gradedecimals'] = 'Decimal places in grades';
 $string['accesscontrol'] = 'Access control';
 $string['aggregategrades'] = 'Re-calculate grades';
 $string['aggregation'] = 'Grades aggregation';
@@ -60,6 +57,8 @@ $string['assignedassessments'] = 'Assigned submissions to assess';
 $string['assignedassessmentsnone'] = 'You have no assigned submission to assess';
 $string['backtoeditform'] = 'Back to editing form';
 $string['byfullname'] = 'by <a href=\"{$a->url}\">{$a->name}</a>';
+$string['calculatetotalgrades'] = 'Calculate total grades';
+$string['calculatetotalgradesdetails'] = 'expected: $a->expected<br />known: $a->known';
 $string['comparisonhigh'] = 'High';
 $string['comparisonlow'] = 'Low';
 $string['comparisonnormal'] = 'Normal';
@@ -68,6 +67,7 @@ $string['comparisonverylow'] = 'Very low';
 $string['configanonymity'] = 'Default anonymity mode in workshops';
 $string['configassessmentcomps'] = 'Default value of the setting that influences the calculation of the grade for assessment.';
 $string['configexamplesmode'] = 'Default mode of examples assessment in workshops';
+$string['configgradedecimals'] = 'Default number of digits that should be shown after the decimal point when displaying grades.';
 $string['configgrade'] = 'Default maximum grade for submission in workshops';
 $string['configgradinggrade'] = 'Default maximum grade for assessment in workshops';
 $string['configmaxbytes'] = 'Default maximum submission file size for all workshops on the site (subject to course limits and other local settings)';
@@ -80,6 +80,7 @@ $string['editingassessmentform'] = 'Editing assessment form';
 $string['editingsubmission'] = 'Editing submission';
 $string['editsubmission'] = 'Edit submission';
 $string['err_removegrademappings'] = 'Unable to remove the unused grade mappings';
+$string['evaluategradeswait'] = 'Please wait until the assessments are evaluated and total grades are calculated';
 $string['examplesbeforeassessment'] = 'Examples are available after own submission and must be assessed before peer/self assessment phase';
 $string['examplesbeforesubmission'] = 'Examples must be assessed before own submission';
 $string['examplesmode'] = 'Mode of examples assessment';
@@ -88,6 +89,7 @@ $string['formatpeergrade'] = '$a->grade ($a->gradinggrade)';
 $string['formatpeergradeover'] = '$a->grade (<del>$a->gradinggrade</del> / <ins>$a->gradinggradeover</ins>)';
 $string['givengrade'] = 'Given grade: $a';
 $string['givengrades'] = 'Given grades';
+$string['gradedecimals'] = 'Decimal places in grades';
 $string['gradinggrade'] = 'Grade for assessment';
 $string['gradinggradeof'] = 'Grade for assessment (of $a)';
 $string['gradingsettings'] = 'Grading settings';
@@ -162,6 +164,7 @@ $string['taskinstructreviewers'] = 'Provide instructions for assessing';
 $string['taskintro'] = 'Set the workshop introduction';
 $string['tasksubmit'] = 'Submit your work';
 $string['teacherweight'] = 'Weight of the teacher\'s assessments';
+$string['totalgradeof'] = 'Total grade (of $a)';
 $string['totalgrade'] = 'Total grade';
 $string['undersetup'] = 'The workshop is currently under setup. Please wait until it is switched to the next phase.';
 $string['useexamplesdesc'] = 'Users practise evaluating on example submissions';
index 8501ef9753f4f039fe0dc8ed2dbd8c2908de154c..47def608e19a45423ec2ce97da3ba5656f221a38 100644 (file)
@@ -165,7 +165,7 @@ class workshop {
      */
     public function get_potential_authors($musthavesubmission=true) {
         $users = get_users_by_capability($this->context, 'mod/workshop:submit',
-                    'u.id,u.lastname,u.firstname', 'u.lastname,u.firstname,u.id', 0, 1000, '', '', false, false, true);
+                    'u.id,u.lastname,u.firstname', 'u.lastname,u.firstname,u.id', '', '', '', '', false, false, true);
         if ($musthavesubmission) {
             $users = array_intersect_key($users, $this->users_with_submission(array_keys($users)));
         }
@@ -183,7 +183,7 @@ class workshop {
      */
     public function get_potential_reviewers($musthavesubmission=false) {
         $users = get_users_by_capability($this->context, 'mod/workshop:peerassess',
-                    'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname,u.id', 0, 1000, '', '', false, false, true);
+                    'u.id, u.lastname, u.firstname', 'u.lastname,u.firstname,u.id', '', '', '', '', false, false, true);
         if ($musthavesubmission) {
             // users without their own submission can not be reviewers
             $users = array_intersect_key($users, $this->users_with_submission(array_keys($users)));
@@ -828,10 +828,36 @@ class workshop {
         }
         $phases[self::PHASE_ASSESSMENT] = $phase;
 
-        // Prepare tasks for the grading evaluation phase - todo
+        // Prepare tasks for the grading evaluation phase
         $phase = new stdClass();
         $phase->title = get_string('phaseevaluation', 'workshop');
         $phase->tasks = array();
+        if (has_capability('mod/workshop:overridegrades', $this->context)) {
+            $authors = $this->get_potential_authors(false);
+            $reviewers = $this->get_potential_reviewers(false);
+            $expected = count($authors + $reviewers);
+            unset($authors);
+            unset($reviewers);
+            $known = $DB->count_records_select('workshop_aggregations', 'workshopid = ? AND totalgrade IS NOT NULL',
+                    array($this->id));
+            $task = new stdClass();
+            $task->title = get_string('calculatetotalgrades', 'workshop');
+            $a = new stdClass();
+            $a->expected    = $expected;
+            $a->known       = $known;
+            $task->details  = get_string('calculatetotalgradesdetails', 'workshop', $a);
+            if ($known >= $expected) {
+                $task->completed = true;
+            } elseif ($this->phase > self::PHASE_EVALUATION) {
+                $task->completed = false;
+            }
+            $phase->tasks['evaluateinfo'] = $task;
+        } else {
+            $task = new stdClass();
+            $task->title = get_string('evaluategradeswait', 'workshop');
+            $task->completed = 'info';
+            $phase->tasks['evaluateinfo'] = $task;
+        }
         $phases[self::PHASE_EVALUATION] = $phase;
 
         // Prepare tasks for the "workshop closed" phase - todo
@@ -1119,6 +1145,7 @@ class workshop {
         $data->totalcount = $numofparticipants;
         $data->maxgrade = $this->real_grade(100);
         $data->maxgradinggrade = $this->real_grading_grade(100);
+        $data->maxtotalgrade = $this->format_total_grade($data->maxgrade + $data->maxgradinggrade);
         return $data;
     }
 
@@ -1150,7 +1177,7 @@ class workshop {
         if (is_null($raw)) {
             return null;
         }
-        return format_float($raw, $this->gradedecimals, $localized);
+        return format_float($raw, $this->gradedecimals, true);
     }
 
     /**
@@ -1294,7 +1321,32 @@ class workshop {
     public function aggregate_total_grades($restrict=null) {
         global $DB;
 
-        // todo
+        // fetch a recordset with all assessments to process
+        $sql = 'SELECT s.grade, s.gradeover, s.authorid AS userid,
+                       ag.id AS agid, ag.gradinggrade, ag.totalgrade
+                  FROM {workshop_submissions} s
+            INNER JOIN {workshop_aggregations} ag ON (ag.userid = s.authorid)
+                 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 ag.userid $usql";
+            $params = array_merge($params, $uparams);
+        } else {
+            throw new coding_exception('Empty value is not a valid parameter here');
+        }
+
+        $sql .= ' ORDER BY ag.userid'; // this is important for bulk processing
+
+        $rs         = $DB->get_recordset_sql($sql, $params);
+
+        foreach ($rs as $current) {
+            $this->aggregate_total_grades_process($current);
+        }
+        $rs->close();
     }
 
     ////////////////////////////////////////////////////////////////////////////
@@ -1334,7 +1386,7 @@ class workshop {
      * was overridden by a teacher, the gradeover value is returned and the rest of grades are ignored.
      *
      * @param array $assessments of stdClass(->submissionid ->submissiongrade ->gradeover ->weight ->grade)
-     * @return null|float the aggregated grade rounded to numeric(10,5)
+     * @return void
      */
     protected function aggregate_submission_grades_process(array $assessments) {
         global $DB;
@@ -1387,7 +1439,7 @@ class workshop {
      * 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)
+     * @return void
      */
     protected function aggregate_grading_grades_process(array $assessments) {
         global $DB;
@@ -1442,6 +1494,49 @@ class workshop {
         }
     }
 
+    /**
+     * Given an object with final grade for submission and final grade for assessment, updates the total grade in DB
+     *
+     * @param stdClass $data
+     * @return void
+     */
+    protected function aggregate_total_grades_process(stdClass $data) {
+        global $DB;
+
+        if (!is_null($data->gradeover)) {
+            $submissiongrade = $data->gradeover;
+        } else {
+            $submissiongrade = $data->grade;
+        }
+
+        // If we do not have enough information to update totalgrade, do not do
+        // anything. Please note there may be a lot of reasons why the workshop
+        // participant does not have one of these grades - maybe she was ill or
+        // just did not reach the deadlines. Teacher has to fix grades in
+        // gradebook manually.
+
+        if (is_null($submissiongrade) or (!empty($this->gradinggrade) and is_null($this->gradinggrade))) {
+            return;
+        }
+
+        $totalgrade = $this->grade * $submissiongrade / 100 + $this->gradinggrade * $data->gradinggrade / 100;
+
+        // check if the new total grade differs from the one stored in the database
+        if (grade_floats_different($totalgrade, $data->totalgrade)) {
+            // we need to save new calculation into the database
+            if (is_null($data->agid)) {
+                // no aggregation record yet
+                $record = new stdClass();
+                $record->workshopid = $this->id;
+                $record->userid = $data->userid;
+                $record->totalgrade = $totalgrade;
+                $DB->insert_record('workshop_aggregations', $record);
+            } else {
+                $DB->set_field('workshop_aggregations', 'totalgrade', $totalgrade, array('id' => $data->agid));
+            }
+        }
+    }
+
     /**
      * Given a list of user ids, returns the filtered one containing just ids of users with own submission
      *
index a9fa5fe6d8ddffa0e4094816ba112430a9b7239e..2fceeb557a7e097270bd76f0a1ac95c0465e5207 100644 (file)
@@ -434,7 +434,8 @@ class moodle_mod_workshop_renderer extends moodle_renderer_base {
                 $this->sortable_heading(get_string('givengrades', 'workshop')),
                 $this->sortable_heading(get_string('gradinggradeof', 'workshop', $data->maxgradinggrade),
                         'gradinggrade', $sortby, $sorthow),
-                $this->sortable_heading(get_string('totalgrade', 'workshop'), 'totalgrade', $sortby, $sorthow),
+                $this->sortable_heading(get_string('totalgradeof', 'workshop', $data->maxtotalgrade),
+                        'totalgrade', $sortby, $sorthow),
             );
         $table->rowclasses  = array();
         $table->colclasses  = array('reviewedby', 'peer', 'reviewerof');
@@ -490,7 +491,11 @@ class moodle_mod_workshop_renderer extends moodle_renderer_base {
                 // column #4 - total grade for submission
                 if ($tr == 0) {
                     $cell = new html_table_cell();
-                    $cell->text = $participant->submissiongrade;
+                    if (is_null($participant->submissiongrade)) {
+                        $cell->text = get_string('nullgrade', 'workshop');
+                    } else {
+                        $cell->text = $participant->submissiongrade;
+                    }
                     $cell->rowspan = $numoftrs;
                     $row->cells[] = $cell;
                 }
@@ -505,7 +510,11 @@ class moodle_mod_workshop_renderer extends moodle_renderer_base {
                 // column #6 - total grade for assessment
                 if ($tr == 0) {
                     $cell = new html_table_cell();
-                    $cell->text = $participant->gradinggrade;
+                    if (is_null($participant->gradinggrade)) {
+                        $cell->text = get_string('nullgrade', 'workshop');
+                    } else {
+                        $cell->text = $participant->gradinggrade;
+                    }
                     $cell->rowspan = $numoftrs;
                     $row->cells[] = $cell;
                 }
@@ -546,7 +555,7 @@ class moodle_mod_workshop_renderer extends moodle_renderer_base {
 
         if (!is_null($sortid)) {
             $iconasc = new moodle_action_icon();
-            $iconasc->image->src = $this->old_icon_url('t/down');
+            $iconasc->image->src = $this->old_icon_url('t/up');
             $iconasc->image->alt = get_string('sortasc', 'workshop');
             $iconasc->image->set_classes('sort asc');
             $newurl = clone($PAGE->url);
@@ -554,7 +563,7 @@ class moodle_mod_workshop_renderer extends moodle_renderer_base {
             $iconasc->link->url = new moodle_url($newurl);
 
             $icondesc = new moodle_action_icon();
-            $icondesc->image->src = $this->old_icon_url('t/up');
+            $icondesc->image->src = $this->old_icon_url('t/down');
             $icondesc->image->alt = get_string('sortdesc', 'workshop');
             $icondesc->image->set_classes('sort desc');
             $newurl = clone($PAGE->url);
index 4e9839450a4043cdf08fcc5980d23b5b8af26e3c..65ea02f734d5dfd15846d2b8afe693ca1df17883 100644 (file)
@@ -49,6 +49,10 @@ class testable_workshop extends workshop {
     public function aggregate_grading_grades_process(array $assessments) {
         parent::aggregate_grading_grades_process($assessments);
     }
+
+    public function aggregate_total_grades_process(stdClass $data) {
+        parent::aggregate_total_grades_process($data);
+    }
 }
 
 /**
@@ -376,7 +380,123 @@ class workshop_internal_api_test extends UnitTestCase {
         $this->workshop->aggregate_grading_grades_process($batch);
     }
 
+    public function test_aggregate_total_grades_process_normal_new() {
+        global $DB;
+
+        // fixture set-up
+        $this->workshop->grade = 85;
+        $this->workshop->gradinggrade = 15;
+
+        $data = new stdClass();
+        $data->agid = 1;
+        $data->grade = 95.00000;
+        $data->gradeover = null;
+        $data->gradinggrade = 67.34500;
+        $data->totalgrade = null;
+
+        $expected = grade_floatval(0.95 * 85 + 0.67345 * 15);
+        $DB->expectOnce('set_field', array('workshop_aggregations', 'totalgrade', $expected, array('id' => 1)));
+        // excercise SUT
+        $this->workshop->aggregate_total_grades_process($data);
+    }
+
+    public function test_aggregate_total_grades_process_overriden_new() {
+        global $DB;
+
+        // fixture set-up
+        $this->workshop->grade = 80;
+        $this->workshop->gradinggrade = 20;
+
+        $data = new stdClass();
+        $data->agid = 2;
+        $data->grade = 95.00000;
+        $data->gradeover = 87.5;
+        $data->gradinggrade = 67.34500;
+        $data->totalgrade = null;
+
+        $expected = grade_floatval(0.875 * 80 + 0.67345 * 20);
+        $DB->expectOnce('set_field', array('workshop_aggregations', 'totalgrade', $expected, array('id' => 2)));
+        // excercise SUT
+        $this->workshop->aggregate_total_grades_process($data);
+    }
+
+    public function test_aggregate_total_grades_process_uptodate() {
+        global $DB;
+
+        // fixture set-up
+        $this->workshop->grade = 100;
+        $this->workshop->gradinggrade = 100;
+
+        $data = new stdClass();
+        $data->agid = 3;
+        $data->grade = 45.00000;
+        $data->gradeover = null;
+        $data->gradinggrade = 40.00000;
+        $data->totalgrade = 85.00000;
+
+        $DB->expectNever('set_field');
+        // excercise SUT
+        $this->workshop->aggregate_total_grades_process($data);
+    }
+
+    public function test_aggregate_total_grades_process_null_grade() {
+        global $DB;
 
+        // fixture set-up
+        $this->workshop->grade = 70;
+        $this->workshop->gradinggrade = 30;
+
+        $data = new stdClass();
+        $data->agid = 4;
+        $data->grade = null;
+        $data->gradeover = null;
+        $data->gradinggrade = 95.00000;
+        $data->totalgrade = null;
+
+        $DB->expectNever('set_field');
+        // excercise SUT
+        $this->workshop->aggregate_total_grades_process($data);
+    }
+
+    /*
+    public function test_aggregate_total_grades_process_null_gradinggrade() {
+        global $DB;
+
+        // fixture set-up
+        $this->workshop->grade = 70;
+        $this->workshop->gradinggrade = 30;
+
+        $data = new stdClass();
+        $data->agid = 5;
+        $data->grade = 12.12345;
+        $data->gradeover = null;
+        $data->gradinggrade = null;
+        $data->totalgrade = null;
+
+        $DB->expectNever('set_field');
+        // excercise SUT
+        $this->workshop->aggregate_total_grades_process($data);
+    }
+
+    public function test_aggregate_total_grades_process_null_gradinggrade_of_zero() {
+        global $DB;
+
+        // fixture set-up
+        $this->workshop->grade = 100;
+        $this->workshop->gradinggrade = 0;
+
+        $data = new stdClass();
+        $data->agid = 6;
+        $data->grade = 56.00000;
+        $data->gradeover = null;
+        $data->gradinggrade = null;
+        $data->totalgrade = null;
+
+        $DB->expectOnce('set_field', array('workshop_aggregations', 'totalgrade', 56.00000, array('id' => 1)));
+        // excercise SUT
+        $this->workshop->aggregate_total_grades_process($data);
+    }
+     */
     public function test_percent_to_value() {
         // fixture setup
         $total = 185;
index 808a01af29c799736045ae98dbcf706ecb204bb5..64c407f5ae325e0b33aa5c2f4f771530709f56e6 100644 (file)
     border: 1px solid #ddd;
 }
 
+.mod-workshop .grading-report td.c3,
+.mod-workshop .grading-report td.c5 {
+    text-align: center;
+    font-size: 160%;
+}
+
+.mod-workshop .grading-report td.c6 {
+    text-align: center;
+    font-size: 220%;
+}
+
 /**
  * Edit assessment form
  */
index 9322fa1bddeb13e7027f12e3cf56822ad6847867..23cdd17e852c3253f77ba5c91128aef46e12f987 100644 (file)
@@ -156,18 +156,18 @@ case workshop::PHASE_ASSESSMENT:
     }
     break;
 case workshop::PHASE_EVALUATION:
-    $page       = optional_param('page', 0, PARAM_INT);
-    $sortby     = optional_param('sortby', 'lastname', PARAM_ALPHA);
-    $sorthow    = optional_param('sorthow', 'ASC', PARAM_ALPHA);
-    $perpage    = 10;           // todo let the user modify this
-    $groups     = '';           // todo let the user choose the group
-    $PAGE->set_url(new moodle_url($PAGE->url, compact('sortby', 'sorthow', 'page')));
-    $data = $workshop->prepare_grading_report($USER->id, $groups, $page, $perpage, $sortby, $sorthow);
-    if ($data) {
-        $showauthornames    = has_capability('mod/workshop:viewauthornames', $PAGE->context);
-        $showreviewernames  = has_capability('mod/workshop:viewreviewernames', $PAGE->context);
+    if (has_capability('mod/workshop:overridegrades', $PAGE->context)) {
+        $page       = optional_param('page', 0, PARAM_INT);
+        $sortby     = optional_param('sortby', 'lastname', PARAM_ALPHA);
+        $sorthow    = optional_param('sorthow', 'ASC', PARAM_ALPHA);
+        $perpage    = 10;           // todo let the user modify this
+        $groups     = '';           // todo let the user choose the group
+        $PAGE->set_url(new moodle_url($PAGE->url, compact('sortby', 'sorthow', 'page')));
+        $data = $workshop->prepare_grading_report($USER->id, $groups, $page, $perpage, $sortby, $sorthow);
+        if ($data) {
+            $showauthornames    = has_capability('mod/workshop:viewauthornames', $PAGE->context);
+            $showreviewernames  = has_capability('mod/workshop:viewreviewernames', $PAGE->context);
 
-        if (has_capability('mod/workshop:overridegrades', $PAGE->context)) {
             $form = new html_form();
             $form->url = new moodle_url($workshop->aggregate_url(), compact('sortby', 'sorthow', 'page'));
             $form->button = new html_button();
@@ -182,7 +182,32 @@ case workshop::PHASE_EVALUATION:
             echo $OUTPUT->box_start('buttonsbar');
             echo $OUTPUT->box($OUTPUT->button($form) . $OUTPUT->help_icon($helpicon), 'buttonwithhelp');
             echo $OUTPUT->box_end();
+
+            // prepare paging bar
+            $pagingbar              = new moodle_paging_bar();
+            $pagingbar->totalcount  = $data->totalcount;
+            $pagingbar->page        = $page;
+            $pagingbar->perpage     = $perpage;
+            $pagingbar->baseurl     = $PAGE->url;
+            $pagingbar->pagevar     = 'page';
+
+            echo $OUTPUT->paging_bar($pagingbar);
+            echo $wsoutput->grading_report($data, $showauthornames, $showreviewernames, $sortby, $sorthow);
+            echo $OUTPUT->paging_bar($pagingbar);
         }
+    }
+    break;
+case workshop::PHASE_CLOSED:
+    $page       = optional_param('page', 0, PARAM_INT);
+    $sortby     = optional_param('sortby', 'lastname', PARAM_ALPHA);
+    $sorthow    = optional_param('sorthow', 'ASC', PARAM_ALPHA);
+    $perpage    = 10;           // todo let the user modify this
+    $groups     = '';           // todo let the user choose the group
+    $PAGE->set_url(new moodle_url($PAGE->url, compact('sortby', 'sorthow', 'page')));
+    $data = $workshop->prepare_grading_report($USER->id, $groups, $page, $perpage, $sortby, $sorthow);
+    if ($data) {
+        $showauthornames    = has_capability('mod/workshop:viewauthornames', $PAGE->context);
+        $showreviewernames  = has_capability('mod/workshop:viewreviewernames', $PAGE->context);
 
         // prepare paging bar
         $pagingbar              = new moodle_paging_bar();
@@ -197,7 +222,6 @@ case workshop::PHASE_EVALUATION:
         echo $OUTPUT->paging_bar($pagingbar);
     }
     break;
-case workshop::PHASE_CLOSED:
 default:
 }