Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/workshop/form/rubric/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 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 require_once(dirname(dirname(__FILE__)) . '/lib.php');  // interface definition
00030 require_once($CFG->libdir . '/gradelib.php');           // to handle float vs decimal issues
00031 
00032 function workshopform_rubric_pluginfile($course, $cm, $context, $filearea, array $args, $forcedownload) {
00033     global $DB;
00034 
00035     if ($context->contextlevel != CONTEXT_MODULE) {
00036         return false;
00037     }
00038 
00039     require_login($course, true, $cm);
00040 
00041     if ($filearea !== 'description') {
00042         return false;
00043     }
00044 
00045     $itemid = (int)array_shift($args); // the id of the assessment form dimension
00046     if (!$workshop = $DB->get_record('workshop', array('id' => $cm->instance))) {
00047         send_file_not_found();
00048     }
00049 
00050     if (!$dimension = $DB->get_record('workshopform_rubric', array('id' => $itemid ,'workshopid' => $workshop->id))) {
00051         send_file_not_found();
00052     }
00053 
00054     // TODO now make sure the user is allowed to see the file
00055     // (media embedded into the dimension description)
00056     $fs = get_file_storage();
00057     $relativepath = implode('/', $args);
00058     $fullpath = "/$context->id/workshopform_rubric/$filearea/$itemid/$relativepath";
00059     if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
00060         return false;
00061     }
00062 
00063     // finally send the file
00064     send_stored_file($file);
00065 }
00066 
00070 class workshop_rubric_strategy implements workshop_strategy {
00071 
00073     const MINDIMS = 3;
00074 
00076     const ADDDIMS = 2;
00077 
00079     protected $workshop;
00080 
00082     protected $dimensions = null;
00083 
00085     protected $descriptionopts;
00086 
00088     protected $definitionopts;
00089 
00091     protected $config;
00092 
00099     public function __construct(workshop $workshop) {
00100         $this->workshop         = $workshop;
00101         $this->dimensions       = $this->load_fields();
00102         $this->config           = $this->load_config();
00103         $this->descriptionopts  = array('trusttext' => true, 'subdirs' => false, 'maxfiles' => -1);
00104         //one day the definitions may become proper wysiwyg fields - not used yet
00105         $this->definitionopts   = array('trusttext' => true, 'subdirs' => false, 'maxfiles' => -1);
00106     }
00107 
00113     public function get_edit_strategy_form($actionurl=null) {
00114         global $CFG;    // needed because the included files use it
00115 
00116         require_once(dirname(__FILE__) . '/edit_form.php');
00117 
00118         $fields             = $this->prepare_form_fields($this->dimensions);
00119         $fields->config_layout = $this->config->layout;
00120 
00121         $nodimensions       = count($this->dimensions);
00122         $norepeatsdefault   = max($nodimensions + self::ADDDIMS, self::MINDIMS);
00123         $norepeats          = optional_param('norepeats', $norepeatsdefault, PARAM_INT);    // number of dimensions
00124         $adddims            = optional_param('adddims', '', PARAM_ALPHA);                   // shall we add more dimensions?
00125         if ($adddims) {
00126             $norepeats += self::ADDDIMS;
00127         }
00128 
00129         // Append editor context to editor options, giving preference to existing context.
00130         $this->descriptionopts = array_merge(array('context' => $this->workshop->context), $this->descriptionopts);
00131 
00132         // prepare the embeded files
00133         for ($i = 0; $i < $nodimensions; $i++) {
00134             // prepare all editor elements
00135             $fields = file_prepare_standard_editor($fields, 'description__idx_'.$i, $this->descriptionopts,
00136                 $this->workshop->context, 'workshopform_rubric', 'description', $fields->{'dimensionid__idx_'.$i});
00137         }
00138 
00139         $customdata = array();
00140         $customdata['workshop'] = $this->workshop;
00141         $customdata['strategy'] = $this;
00142         $customdata['norepeats'] = $norepeats;
00143         $customdata['descriptionopts'] = $this->descriptionopts;
00144         $customdata['current']  = $fields;
00145         $attributes = array('class' => 'editstrategyform');
00146 
00147         return new workshop_edit_rubric_strategy_form($actionurl, $customdata, 'post', '', $attributes);
00148     }
00149 
00162     public function save_edit_strategy_form(stdclass $data) {
00163         global $DB;
00164 
00165         $norepeats  = $data->norepeats;
00166         $layout     = $data->config_layout;
00167         $data       = $this->prepare_database_fields($data);
00168         $deletedims = array();  // dimension ids to be deleted
00169         $deletelevs = array();  // level ids to be deleted
00170 
00171         if ($DB->record_exists('workshopform_rubric_config', array('workshopid' => $this->workshop->id))) {
00172             $DB->set_field('workshopform_rubric_config', 'layout', $layout, array('workshopid' => $this->workshop->id));
00173         } else {
00174             $record = new stdclass();
00175             $record->workshopid = $this->workshop->id;
00176             $record->layout     = $layout;
00177             $DB->insert_record('workshopform_rubric_config', $record, false);
00178         }
00179 
00180         foreach ($data as $record) {
00181             if (0 == strlen(trim($record->description_editor['text']))) {
00182                 if (!empty($record->id)) {
00183                     // existing record with empty description - to be deleted
00184                     $deletedims[] = $record->id;
00185                     foreach ($record->levels as $level) {
00186                         if (!empty($level->id)) {
00187                             $deletelevs[] = $level->id;
00188                         }
00189                     }
00190                 }
00191                 continue;
00192             }
00193             if (empty($record->id)) {
00194                 // new field
00195                 $record->id = $DB->insert_record('workshopform_rubric', $record);
00196             } else {
00197                 // exiting field
00198                 $DB->update_record('workshopform_rubric', $record);
00199             }
00200             // re-save with correct path to embeded media files
00201             $record = file_postupdate_standard_editor($record, 'description', $this->descriptionopts,
00202                                                       $this->workshop->context, 'workshopform_rubric', 'description', $record->id);
00203             $DB->update_record('workshopform_rubric', $record);
00204 
00205             // create/update the criterion levels
00206             foreach ($record->levels as $level) {
00207                 $level->dimensionid = $record->id;
00208                 if (0 == strlen(trim($level->definition))) {
00209                     if (!empty($level->id)) {
00210                         $deletelevs[] = $level->id;
00211                     }
00212                     continue;
00213                 }
00214                 if (empty($level->id)) {
00215                     // new field
00216                     $level->id = $DB->insert_record('workshopform_rubric_levels', $level);
00217                 } else {
00218                     // exiting field
00219                     $DB->update_record('workshopform_rubric_levels', $level);
00220                 }
00221             }
00222         }
00223         $DB->delete_records_list('workshopform_rubric_levels', 'id', $deletelevs);
00224         $this->delete_dimensions($deletedims);
00225     }
00226 
00236     public function get_assessment_form(moodle_url $actionurl=null, $mode='preview', stdclass $assessment=null, $editable=true, $options=array()) {
00237         global $CFG;    // needed because the included files use it
00238         global $DB;
00239         require_once(dirname(__FILE__) . '/assessment_form.php');
00240 
00241         $fields         = $this->prepare_form_fields($this->dimensions);
00242         $nodimensions   = count($this->dimensions);
00243 
00244         // rewrite URLs to the embeded files
00245         for ($i = 0; $i < $nodimensions; $i++) {
00246             $fields->{'description__idx_'.$i} = file_rewrite_pluginfile_urls($fields->{'description__idx_'.$i},
00247                 'pluginfile.php', $this->workshop->context->id, 'workshopform_rubric', 'description',
00248                 $fields->{'dimensionid__idx_'.$i});
00249 
00250         }
00251 
00252         if ('assessment' === $mode and !empty($assessment)) {
00253             // load the previously saved assessment data
00254             $grades = $this->get_current_assessment_data($assessment);
00255             $current = new stdclass();
00256             for ($i = 0; $i < $nodimensions; $i++) {
00257                 $dimid = $fields->{'dimensionid__idx_'.$i};
00258                 if (isset($grades[$dimid])) {
00259                     $givengrade = $grades[$dimid]->grade;
00260                     // find a level with this grade
00261                     $levelid = null;
00262                     foreach ($this->dimensions[$dimid]->levels as $level) {
00263                         if (grade_floats_equal($level->grade, $givengrade)) {
00264                             $levelid = $level->id;
00265                             break;
00266                         }
00267                     }
00268                     $current->{'gradeid__idx_'.$i}       = $grades[$dimid]->id;
00269                     $current->{'chosenlevelid__idx_'.$i} = $levelid;
00270                 }
00271             }
00272         }
00273 
00274         // set up the required custom data common for all strategies
00275         $customdata['strategy'] = $this;
00276         $customdata['workshop'] = $this->workshop;
00277         $customdata['mode']     = $mode;
00278         $customdata['options']  = $options;
00279 
00280         // set up strategy-specific custom data
00281         $customdata['nodims']   = $nodimensions;
00282         $customdata['fields']   = $fields;
00283         $customdata['current']  = isset($current) ? $current : null;
00284         $attributes = array('class' => 'assessmentform rubric ' . $this->config->layout);
00285 
00286         $formclassname = 'workshop_rubric_' . $this->config->layout . '_assessment_form';
00287         return new $formclassname($actionurl, $customdata, 'post', '', $attributes, $editable);
00288     }
00289 
00299     public function save_assessment(stdclass $assessment, stdclass $data) {
00300         global $DB;
00301 
00302         for ($i = 0; isset($data->{'dimensionid__idx_' . $i}); $i++) {
00303             $grade = new stdclass();
00304             $grade->id = $data->{'gradeid__idx_' . $i};
00305             $grade->assessmentid = $assessment->id;
00306             $grade->strategy = 'rubric';
00307             $grade->dimensionid = $data->{'dimensionid__idx_' . $i};
00308             $chosenlevel = $data->{'chosenlevelid__idx_'.$i};
00309             $grade->grade = $this->dimensions[$grade->dimensionid]->levels[$chosenlevel]->grade;
00310 
00311             if (empty($grade->id)) {
00312                 // new grade
00313                 $grade->id = $DB->insert_record('workshop_grades', $grade);
00314             } else {
00315                 // updated grade
00316                 $DB->update_record('workshop_grades', $grade);
00317             }
00318         }
00319         return $this->update_peer_grade($assessment);
00320     }
00321 
00327     public function form_ready() {
00328         if (count($this->dimensions) > 0) {
00329             return true;
00330         }
00331         return false;
00332     }
00333 
00337     public function get_assessments_recordset($restrict=null) {
00338         global $DB;
00339 
00340         $sql = 'SELECT s.id AS submissionid,
00341                        a.id AS assessmentid, a.weight AS assessmentweight, a.reviewerid, a.gradinggrade,
00342                        g.dimensionid, g.grade
00343                   FROM {workshop_submissions} s
00344                   JOIN {workshop_assessments} a ON (a.submissionid = s.id)
00345                   JOIN {workshop_grades} g ON (g.assessmentid = a.id AND g.strategy = :strategy)
00346                  WHERE s.example=0 AND s.workshopid=:workshopid'; // to be cont.
00347         $params = array('workshopid' => $this->workshop->id, 'strategy' => $this->workshop->strategy);
00348 
00349         if (is_null($restrict)) {
00350             // update all users - no more conditions
00351         } elseif (!empty($restrict)) {
00352             list($usql, $uparams) = $DB->get_in_or_equal($restrict, SQL_PARAMS_NAMED);
00353             $sql .= " AND a.reviewerid $usql";
00354             $params = array_merge($params, $uparams);
00355         } else {
00356             throw new coding_exception('Empty value is not a valid parameter here');
00357         }
00358 
00359         $sql .= ' ORDER BY s.id'; // this is important for bulk processing
00360 
00361         return $DB->get_recordset_sql($sql, $params);
00362     }
00363 
00367     public function get_dimensions_info() {
00368         global $DB;
00369 
00370         $sql = 'SELECT d.id AS id, MIN(l.grade) AS min, MAX(l.grade) AS max, 1 AS weight
00371                   FROM {workshopform_rubric} d
00372             INNER JOIN {workshopform_rubric_levels} l ON (d.id = l.dimensionid)
00373                  WHERE d.workshopid = :workshopid
00374               GROUP BY d.id';
00375         $params = array('workshopid' => $this->workshop->id);
00376 
00377         return $DB->get_records_sql($sql, $params);
00378     }
00379 
00389     public static function scale_used($scaleid, $workshopid=null) {
00390         return false;
00391     }
00392 
00400     public static function delete_instance($workshopid) {
00401         global $DB;
00402 
00403         $dimensions = $DB->get_records('workshopform_rubric', array('workshopid' => $workshopid), '', 'id');
00404         $DB->delete_records_list('workshopform_rubric_levels', 'dimensionid', array_keys($dimensions));
00405         $DB->delete_records('workshopform_rubric', array('workshopid' => $workshopid));
00406         $DB->delete_records('workshopform_rubric_config', array('workshopid' => $workshopid));
00407     }
00408 
00410     // Internal methods                                                           //
00412 
00418     protected function load_fields() {
00419         global $DB;
00420 
00421         $sql = 'SELECT l.id AS lid, r.id AS rid, r.*, l.*
00422                   FROM {workshopform_rubric} r
00423              LEFT JOIN {workshopform_rubric_levels} l ON (l.dimensionid = r.id)
00424                  WHERE r.workshopid = :workshopid
00425                  ORDER BY r.sort, l.grade';
00426         $params = array('workshopid' => $this->workshop->id);
00427 
00428         $records = $DB->get_records_sql($sql, $params);
00429         $fields = array();
00430         foreach ($records as $record) {
00431             if (!isset($fields[$record->rid])) {
00432                 $fields[$record->rid] = new stdclass();
00433                 $fields[$record->rid]->id = $record->rid;
00434                 $fields[$record->rid]->sort = $record->sort;
00435                 $fields[$record->rid]->description = $record->description;
00436                 $fields[$record->rid]->descriptionformat = $record->descriptionformat;
00437                 $fields[$record->rid]->levels = array();
00438             }
00439             if (!empty($record->lid)) {
00440                 $fields[$record->rid]->levels[$record->lid] = new stdclass();
00441                 $fields[$record->rid]->levels[$record->lid]->id = $record->lid;
00442                 $fields[$record->rid]->levels[$record->lid]->grade = $record->grade;
00443                 $fields[$record->rid]->levels[$record->lid]->definition = $record->definition;
00444                 $fields[$record->rid]->levels[$record->lid]->definitionformat = $record->definitionformat;
00445             }
00446         }
00447         return $fields;
00448     }
00449 
00455     protected function load_config() {
00456         global $DB;
00457 
00458         if (!$config = $DB->get_record('workshopform_rubric_config', array('workshopid' => $this->workshop->id), 'layout')) {
00459             $config = (object)array('layout' => 'list');
00460         }
00461         return $config;
00462     }
00463 
00470     protected function prepare_form_fields(array $fields) {
00471 
00472         $formdata = new stdclass();
00473         $key = 0;
00474         foreach ($fields as $field) {
00475             $formdata->{'dimensionid__idx_' . $key}             = $field->id;
00476             $formdata->{'description__idx_' . $key}             = $field->description;
00477             $formdata->{'description__idx_' . $key.'format'}    = $field->descriptionformat;
00478             $formdata->{'numoflevels__idx_' . $key}             = count($field->levels);
00479             $lev = 0;
00480             foreach ($field->levels as $level) {
00481                 $formdata->{'levelid__idx_' . $key . '__idy_' . $lev} = $level->id;
00482                 $formdata->{'grade__idx_' . $key . '__idy_' . $lev} = $level->grade;
00483                 $formdata->{'definition__idx_' . $key . '__idy_' . $lev} = $level->definition;
00484                 $formdata->{'definition__idx_' . $key . '__idy_' . $lev . 'format'} = $level->definitionformat;
00485                 $lev++;
00486             }
00487             $key++;
00488         }
00489         return $formdata;
00490     }
00491 
00500     protected function delete_dimensions(array $ids) {
00501         global $DB;
00502 
00503         $fs = get_file_storage();
00504         foreach ($ids as $id) {
00505             if (!empty($id)) {   // to prevent accidental removal of all files in the area
00506                 $fs->delete_area_files($this->workshop->context->id, 'workshopform_rubric', 'description', $id);
00507             }
00508         }
00509         $DB->delete_records_list('workshopform_rubric', 'id', $ids);
00510     }
00511 
00523     protected function prepare_database_fields(stdclass $raw) {
00524 
00525         $cook = array();
00526 
00527         for ($i = 0; $i < $raw->norepeats; $i++) {
00528             $cook[$i]                       = new stdclass();
00529             $cook[$i]->id                   = $raw->{'dimensionid__idx_'.$i};
00530             $cook[$i]->workshopid           = $this->workshop->id;
00531             $cook[$i]->sort                 = $i + 1;
00532             $cook[$i]->description_editor   = $raw->{'description__idx_'.$i.'_editor'};
00533             $cook[$i]->levels               = array();
00534 
00535             $j = 0;
00536             while (isset($raw->{'levelid__idx_' . $i . '__idy_' . $j})) {
00537                 $level                      = new stdclass();
00538                 $level->id                  = $raw->{'levelid__idx_' . $i . '__idy_' . $j};
00539                 $level->grade               = $raw->{'grade__idx_'.$i.'__idy_'.$j};
00540                 $level->definition          = $raw->{'definition__idx_'.$i.'__idy_'.$j};
00541                 $level->definitionformat    = FORMAT_HTML;
00542                 $cook[$i]->levels[]         = $level;
00543                 $j++;
00544             }
00545         }
00546 
00547         return $cook;
00548     }
00549 
00556     protected function get_current_assessment_data(stdclass $assessment) {
00557         global $DB;
00558 
00559         if (empty($this->dimensions)) {
00560             return array();
00561         }
00562         list($dimsql, $dimparams) = $DB->get_in_or_equal(array_keys($this->dimensions), SQL_PARAMS_NAMED);
00563         // beware! the caller may rely on the returned array is indexed by dimensionid
00564         $sql = "SELECT dimensionid, wg.*
00565                   FROM {workshop_grades} wg
00566                  WHERE assessmentid = :assessmentid AND strategy= :strategy AND dimensionid $dimsql";
00567         $params = array('assessmentid' => $assessment->id, 'strategy' => 'rubric');
00568         $params = array_merge($params, $dimparams);
00569 
00570         return $DB->get_records_sql($sql, $params);
00571     }
00572 
00579     protected function update_peer_grade(stdclass $assessment) {
00580         $grades     = $this->get_current_assessment_data($assessment);
00581         $suggested  = $this->calculate_peer_grade($grades);
00582         if (!is_null($suggested)) {
00583             $this->workshop->set_peer_grade($assessment->id, $suggested);
00584         }
00585         return $suggested;
00586     }
00587 
00595     protected function calculate_peer_grade(array $grades) {
00596 
00597         if (empty($grades)) {
00598             return null;
00599         }
00600 
00601         // summarize the grades given in rubrics
00602         $sumgrades  = 0;
00603         foreach ($grades as $grade) {
00604             $sumgrades += $grade->grade;
00605         }
00606 
00607         // get the minimal and maximal possible grade (sum of minimal/maximal grades across all dimensions)
00608         $mingrade = 0;
00609         $maxgrade = 0;
00610         foreach ($this->dimensions as $dimension) {
00611             $mindimensiongrade = null;
00612             $maxdimensiongrade = null;
00613             foreach ($dimension->levels as $level) {
00614                 if (is_null($mindimensiongrade) or $level->grade < $mindimensiongrade) {
00615                     $mindimensiongrade = $level->grade;
00616                 }
00617                 if (is_null($maxdimensiongrade) or $level->grade > $maxdimensiongrade) {
00618                     $maxdimensiongrade = $level->grade;
00619                 }
00620             }
00621             $mingrade += $mindimensiongrade;
00622             $maxgrade += $maxdimensiongrade;
00623         }
00624 
00625         if ($maxgrade - $mingrade > 0) {
00626             return grade_floatval(100 * ($sumgrades - $mingrade) / ($maxgrade - $mingrade));
00627         } else {
00628             return null;
00629         }
00630     }
00631 }
 All Data Structures Namespaces Files Functions Variables Enumerations