Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/workshop/eval/best/lib.php
Go to the documentation of this file.
00001 <?php
00002 
00003 // This file is part of Moodle - http://moodle.org/
00004 //
00005 // Moodle is free software: you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation, either version 3 of the License, or
00008 // (at your option) any later version.
00009 //
00010 // Moodle is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00017 
00028 defined('MOODLE_INTERNAL') || die();
00029 
00030 require_once(dirname(dirname(__FILE__)) . '/lib.php');  // interface definition
00031 require_once($CFG->libdir . '/gradelib.php');
00032 
00036 class workshop_best_evaluation implements workshop_evaluation {
00037 
00039     protected $workshop;
00040 
00042     protected $settings;
00043 
00050     public function __construct(workshop $workshop) {
00051         global $DB;
00052         $this->workshop = $workshop;
00053         $this->settings = $DB->get_record('workshopeval_best_settings', array('workshopid' => $this->workshop->id));
00054     }
00055 
00067     public function update_grading_grades(stdclass $settings, $restrict=null) {
00068         global $DB;
00069 
00070         // remember the recently used settings for this workshop
00071         if (empty($this->settings)) {
00072             $record = new stdclass();
00073             $record->workshopid = $this->workshop->id;
00074             $record->comparison = $settings->comparison;
00075             $DB->insert_record('workshopeval_best_settings', $record);
00076         } elseif ($this->settings->comparison != $settings->comparison) {
00077             $DB->set_field('workshopeval_best_settings', 'comparison', $settings->comparison,
00078                     array('workshopid' => $this->workshop->id));
00079         }
00080 
00081         // get the grading strategy instance
00082         $grader = $this->workshop->grading_strategy_instance();
00083 
00084         // get the information about the assessment dimensions
00085         $diminfo = $grader->get_dimensions_info();
00086 
00087         // fetch a recordset with all assessments to process
00088         $rs         = $grader->get_assessments_recordset($restrict);
00089         $batch      = array();    // will contain a set of all assessments of a single submission
00090         $previous   = null;       // a previous record in the recordset
00091         foreach ($rs as $current) {
00092             if (is_null($previous)) {
00093                 // we are processing the very first record in the recordset
00094                 $previous = $current;
00095             }
00096             if ($current->submissionid == $previous->submissionid) {
00097                 $batch[] = $current;
00098             } else {
00099                 // process all the assessments of a single submission
00100                 $this->process_assessments($batch, $diminfo, $settings);
00101                 // start with a new batch to be processed
00102                 $batch = array($current);
00103                 $previous = $current;
00104             }
00105         }
00106         // do not forget to process the last batch!
00107         $this->process_assessments($batch, $diminfo, $settings);
00108         $rs->close();
00109     }
00110 
00116     public function get_settings_form(moodle_url $actionurl=null) {
00117         global $CFG;    // needed because the included files use it
00118         global $DB;
00119         require_once(dirname(__FILE__) . '/settings_form.php');
00120 
00121         $customdata['workshop'] = $this->workshop;
00122         $customdata['current'] = $this->settings;
00123         $attributes = array('class' => 'evalsettingsform best');
00124 
00125         return new workshop_best_evaluation_settings_form($actionurl, $customdata, 'post', '', $attributes);
00126     }
00127 
00135     public static function delete_instance($workshopid) {
00136         global $DB;
00137 
00138         $DB->delete_records('workshopeval_best_settings', array('workshopid' => $workshopid));
00139     }
00140 
00142     // Internal methods                                                           //
00144 
00153     protected function process_assessments(array $assessments, array $diminfo, stdclass $settings) {
00154         global $DB;
00155 
00156         if (empty($assessments)) {
00157             return;
00158         }
00159 
00160         // reindex the passed flat structure to be indexed by assessmentid
00161         $assessments = $this->prepare_data_from_recordset($assessments);
00162 
00163         // normalize the dimension grades to the interval 0 - 100
00164         $assessments = $this->normalize_grades($assessments, $diminfo);
00165 
00166         // get a hypothetical average assessment
00167         $average = $this->average_assessment($assessments);
00168 
00169         // calculate variance of dimension grades
00170         $variances = $this->weighted_variance($assessments);
00171         foreach ($variances as $dimid => $variance) {
00172             $diminfo[$dimid]->variance = $variance;
00173         }
00174 
00175         // for every assessment, calculate its distance from the average one
00176         $distances = array();
00177         foreach ($assessments as $asid => $assessment) {
00178             $distances[$asid] = $this->assessments_distance($assessment, $average, $diminfo, $settings);
00179         }
00180 
00181         // identify the best assessments - that is those with the shortest distance from the best assessment
00182         $bestids = array_keys($distances, min($distances));
00183 
00184         // for every assessment, calculate its distance from the nearest best assessment
00185         $distances = array();
00186         foreach ($bestids as $bestid) {
00187             $best = $assessments[$bestid];
00188             foreach ($assessments as $asid => $assessment) {
00189                 $d = $this->assessments_distance($assessment, $best, $diminfo, $settings);
00190                 if (!is_null($d) and (!isset($distances[$asid]) or $d < $distances[$asid])) {
00191                     $distances[$asid] = $d;
00192                 }
00193             }
00194         }
00195 
00196         // calculate the grading grade
00197         foreach ($distances as $asid => $distance) {
00198             $gradinggrade = (100 - $distance);
00199             if ($gradinggrade < 0) {
00200                 $gradinggrade = 0;
00201             }
00202             if ($gradinggrade > 100) {
00203                 $gradinggrade = 100;
00204             }
00205             $grades[$asid] = grade_floatval($gradinggrade);
00206         }
00207 
00208         // if the new grading grade differs from the one stored in database, update it
00209         // we do not use set_field() here because we want to pass $bulk param
00210         foreach ($grades as $assessmentid => $grade) {
00211             if (grade_floats_different($grade, $assessments[$assessmentid]->gradinggrade)) {
00212                 // the value has changed
00213                 $record = new stdclass();
00214                 $record->id = $assessmentid;
00215                 $record->gradinggrade = grade_floatval($grade);
00216                 // do not set timemodified here, it contains the timestamp of when the form was
00217                 // saved by the peer reviewer, not when it was aggregated
00218                 $DB->update_record('workshop_assessments', $record, true);  // bulk operations expected
00219             }
00220         }
00221 
00222         // done. easy, heh? ;-)
00223     }
00224 
00231     protected function prepare_data_from_recordset($assessments) {
00232         $data = array();    // to be returned
00233         foreach ($assessments as $a) {
00234             $id = $a->assessmentid; // just an abbreviation
00235             if (!isset($data[$id])) {
00236                 $data[$id] = new stdclass();
00237                 $data[$id]->assessmentid = $a->assessmentid;
00238                 $data[$id]->weight       = $a->assessmentweight;
00239                 $data[$id]->reviewerid   = $a->reviewerid;
00240                 $data[$id]->gradinggrade = $a->gradinggrade;
00241                 $data[$id]->submissionid = $a->submissionid;
00242                 $data[$id]->dimgrades    = array();
00243             }
00244             $data[$id]->dimgrades[$a->dimensionid] = $a->grade;
00245         }
00246         return $data;
00247     }
00248 
00259     protected function normalize_grades(array $assessments, array $diminfo) {
00260         foreach ($assessments as $asid => $assessment) {
00261             foreach ($assessment->dimgrades as $dimid => $dimgrade) {
00262                 $dimmin = $diminfo[$dimid]->min;
00263                 $dimmax = $diminfo[$dimid]->max;
00264                 if ($dimmin == $dimmax) {
00265                     $assessment->dimgrades[$dimid] = grade_floatval($dimmax);
00266                 } else {
00267                     $assessment->dimgrades[$dimid] = grade_floatval(($dimgrade - $dimmin) / ($dimmax - $dimmin) * 100);
00268                 }
00269             }
00270         }
00271         return $assessments;
00272     }
00273 
00282     protected function average_assessment(array $assessments) {
00283         $sumdimgrades = array();
00284         foreach ($assessments as $a) {
00285             foreach ($a->dimgrades as $dimid => $dimgrade) {
00286                 if (!isset($sumdimgrades[$dimid])) {
00287                     $sumdimgrades[$dimid] = 0;
00288                 }
00289                 $sumdimgrades[$dimid] += $dimgrade * $a->weight;
00290             }
00291         }
00292 
00293         $sumweights = 0;
00294         foreach ($assessments as $a) {
00295             $sumweights += $a->weight;
00296         }
00297         if ($sumweights == 0) {
00298             // unable to calculate average assessment
00299             return null;
00300         }
00301 
00302         $average = new stdclass();
00303         $average->dimgrades = array();
00304         foreach ($sumdimgrades as $dimid => $sumdimgrade) {
00305             $average->dimgrades[$dimid] = grade_floatval($sumdimgrade / $sumweights);
00306         }
00307         return $average;
00308     }
00309 
00322     protected function weighted_variance(array $assessments) {
00323         $first = reset($assessments);
00324         if (empty($first)) {
00325             return null;
00326         }
00327         $dimids = array_keys($first->dimgrades);
00328         $asids  = array_keys($assessments);
00329         $vars   = array();  // to be returned
00330         foreach ($dimids as $dimid) {
00331             $n = 0;
00332             $s = 0;
00333             $sumweight = 0;
00334             foreach ($asids as $asid) {
00335                 $x = $assessments[$asid]->dimgrades[$dimid];    // value (data point)
00336                 $weight = $assessments[$asid]->weight;          // the values' weight
00337                 if ($weight == 0) {
00338                     continue;
00339                 }
00340                 if ($n == 0) {
00341                     $n = 1;
00342                     $mean = $x;
00343                     $s = 0;
00344                     $sumweight = $weight;
00345                 } else {
00346                     $n++;
00347                     $temp = $weight + $sumweight;
00348                     $q = $x - $mean;
00349                     $r = $q * $weight / $temp;
00350                     $s = $s + $sumweight * $q * $r;
00351                     $mean = $mean + $r;
00352                     $sumweight = $temp;
00353                 }
00354             }
00355             if ($sumweight > 0 and $n > 1) {
00356                 // for the sample: $vars[$dimid] = ($s * $n) / (($n - 1) * $sumweight);
00357                 // for the population:
00358                 $vars[$dimid] = $s / $sumweight;
00359             } else {
00360                 $vars[$dimid] = null;
00361             }
00362         }
00363         return $vars;
00364     }
00365 
00381     protected function assessments_distance(stdclass $assessment, stdclass $referential, array $diminfo, stdclass $settings) {
00382         $distance = 0;
00383         $n = 0;
00384         foreach (array_keys($assessment->dimgrades) as $dimid) {
00385             $agrade = $assessment->dimgrades[$dimid];
00386             $rgrade = $referential->dimgrades[$dimid];
00387             $var    = $diminfo[$dimid]->variance;
00388             $weight = $diminfo[$dimid]->weight;
00389             $n     += $weight;
00390 
00391             // variations very close to zero are too sensitive to a small change of data values
00392             if ($var > 0.01 and $agrade != $rgrade) {
00393                 $absdelta   = abs($agrade - $rgrade);
00394                 $reldelta   = pow($agrade - $rgrade, 2) / ($settings->comparison * $var);
00395                 $distance  += $absdelta * $reldelta * $weight;
00396             }
00397         }
00398         if ($n > 0) {
00399             // average distance across all dimensions
00400             return round($distance / $n, 4);
00401         } else {
00402             return null;
00403         }
00404     }
00405 }
 All Data Structures Namespaces Files Functions Variables Enumerations